您的位置:首页 > 其它

提高系统性能的2个常用手段---缓存和异步

2017-09-14 09:48 330 查看

缓存

缓存

1.1 缓存刷新机制,缓存刷新是指什么时候把数据库中的数据加载到缓存

(1) 定期刷新;

(2) 缓存命中失败时刷新;

1.2 在有缓存时的数据写入方式:

(1) 同步写入,即缓存和数据库同时被写入,即在应用层进行双写操作,这种方式可以有效保证缓存和DB中数据的一致性,由于这种方式即要更新缓存同时还要更改数据库,其访问效率相对较低,适合读多写少的场景;

(2) 异步更新机制,在写操作时只写入缓存便返回,写数据库的操作交个另外一个异步操作来完成,这样可以加快用户请求的处理速度,进而增强整个系统的并发量,但是这种方式会造成在一定时间内缓存和数据库中的数据不一致;

一般情况下,同时保证数据写入缓存和数据库的事务性是非常困难的,因为这时涉及到写入数据到两种系统中,而且一般缓存不提供回滚机制,如果系统的要求不严格,则可采用一些妥协措施,以提升系统的整体访问效率。

1.3 缓存使用的几种方式:

(1) 全量缓存,是指所有的数据在缓存中都有,它不适合数据量较大、冷热访问极度不均匀的情况,这些情况下要么缓存放不下这么多数据,要么缓存中加载的都是大量不用的数据,造成缓存浪费,同时也会影响访问效率;它适合数据量小并且全部数据都会经常被访问的场景;例如,我们有一个业务是获取系统中各子服务的地址,在每个客户端上线时第一步操作就是获取各子服务的地址,然后才能进行真正的业务操作,这些服务地址信息数据量不大,但是全部都会经常被访问,因此它们需要被全量缓存,为保障缓存和数据库的一致性,我们采用双写策略,更新服务地址时要同时更新缓存和数据库;

(2) 只缓存热点数据,这种方式适合数据量大、冷热访问不均匀的场景,该方式涉及到数据何时被加载、何时被逐出的问题;加载时可以采用两种方式:增量方式,服务启动时不向缓存中加载任何数据,等访问时在缓存找不到再去数据库中取并将取出的结果加载到缓存中,这样随着时间的推移,热点数据将被加载到缓存中;另一种方式是减量方式,服务启动时加载全部的数据,一定时间内不被使用的数据将会被逐出,随着时间的推移冷数据都被逐出,而留下来的都是热点数据;例如:我们为一个IM系统设计账号服务子系统时,就采用只缓存热点数据的方式,并且数据加载方式采用增量方式;只缓存热点数据的方式要特别注意缓存的量,如果缓存中没有存下足够的热点数据,那么将会有一定量的请求会穿透缓存,将压力施加到数据库上;

1.4 架构设计时的三个层次的缓存

(1)对象缓存,例如Redis、Memched等,它以Key-Value的方式帮助我们缓存一些对象数据,Redis提供的Value类型更多,使用时更加方便;但是这些缓存通常不提供数据库的事务性,即它不满足ACID的四点要求,在分布式系统中我们更常提及的是CAP特性,即在分布式环境中无法同时满足:一致性、可用性和分区容忍性,因此,在做分布式系统中要对业务进行更细粒度的分析,确定哪些数据需要事务性存储,哪些只需CAP中的两点即可满足?按照这种思路,一般一个系统中只有极少数核心的数据要求事务性,绝大多数数据可能要求最终一致性等等。

(2)应用缓存,例如代理缓存和反向代理缓存,应用缓存的目的是加快用户的访问速度和尽量降低对资源的使用率;代理缓存的例子,例如互联网服务提供商(ISP)缓存了一部分常用URL对于的资源,当用户访问这些URL时,ISP直接从缓存中把结果返回给用户,而不是将用户请求转交给URL所指向的真正服务器;反向代理的例子,在服务前面部署一个nginx用于缓存静态页面,当用户访问这些静态页面时,反向代理nginx直接将缓存的静态页面返回给用户,这些请求就不会落到后面的真正服务器上,反向代理服务对访问服务后台的所有请求进行了过滤、拦截,大大降低了请求对后端的压力;

(3)内容交付网络缓存,最典型的例子就是CDN,在视频点播网站中,如果我们点播的电影都在中央的资源服务器中,那么这个服务的性能再强大也难以为用户提供流畅的视频服务,采用CDN之后,就可以根据用户的访问情况,将热点数据推送到距离用户最近的边缘资源服务器上,这样用户在观看视频时就直接从边缘资源服务器上进行下载,这样资源离用户更近,下载速度更快;

1.5 个人理解,在进行高并发服务的设计时,应该采用逐级分流的策略,将用户的请求呈树状分流到各个服务中,用户经常访问的请求被放在树状服务的上层,业务处理越复杂的请求被放在树状服务的下层;如下图所示:



异步

同步与互斥,提到异步必然要涉及到与之对应的另一个词“同步”,而提到“同步”很多人也会联想到另一个词“互斥”,同步是指多个操作之间产生了依赖或者先后顺序关系,互斥是指多个操作需要访问同一个资源,而这个资源又不能让多个操作同时进行,那么这多个操作之间就是互斥关系;
同步调用与异步调用,在服务设计时,同步调用是指调用方在发起调用之后必须一直等待直到调用结果返回才能进行后续的操作,异步调用是指调用发起方在发起调用之后立即返回继续执行后续的操作,而不需要等待调用的返回结果。举个简单的例子,同步就像打电话,异步就像发邮件;
同步调用和异步调用,二者各有其应用场景,如果相邻两个操作之间存在依赖关系,那么就必须选择同步调用,否则都可以采用异步,但是同步调用更为简单,如果在一个系统中滥用异步会造成系统复杂度增加;从扩展性角度来看,同步容易造成调用阻塞,异步调用更易于扩展,而且异步调用方式天然具备故障的隔离性,因此,在合适的场景选择同步能让你的系统更为简单,在合适的场景选择异步能让你的系统更具备扩展性;

例如,我们要设计一个数据收集和处理系统,该系统分为两个子服务:数据收集服务和数据处理服务,数据收集服务用于从客户端收集各种上报信息,客户端上报的所有数据需符合系统的格式要求(上报的消息必须为JSON格式字符串,在字符串中必须包含version、type、con三个字段,上报的不同消息的type的值不一样,上报的消息内容存储在con字段中),数据处理程序对收集服务传入的数据进行分类型处理,并根据type字段值存入数据库的不同表中,如果系统被设计成同步的方式,那么它会像像下图所示:



在同步的系统中,数据收集服务没收到一个客户端上报的消息就调用数据分析服务对该消息进行处理,数据分析服务将数据解析、转换之后存入数据库,然后将结果返回给数据收集服务,然后数据收集服务再将结果返回到客户端,那么在这个系统中数据收集服务调用数据分析服务是同步调用,因为它要等待数据分析服务返回的结果,而数据分析服务调用数据库也是同步调用,因为数据分析服务也要等待数据库的处理结果;

在上述设计的数据收集、分析系统中,有如下问题:(1)扩展性受影响,客户端并发量受到数据分析服务、数据库存储速度的影响;(2)系统可用性受影响,一般数据分析服务所做的事情稍微复杂,一旦这个服务出现问题,整个数据收集、分析系统都无法正常工作;

为解决上述同步过程中出现的问题,将系统改造成如下图所示:



我们引入一个异步队列(例如RabbitMQ)用于对“数据收集服务”和“数据分析服务”的解耦,异步队列可以对数据进行缓存并具备持久化能力,数据收集服务在受到客户端上报的数据时,只检查其格式是否符合要求(是否为JSON串?JSON中是否包含了version、type和con三个字段),检测通过的数据发送到异步队列中,数据一旦放入异步队列就立即返回;数据分析服务从异步队列中拉取消息,并对消息进行分析、处理然后将数据存储到数据库的相应表中;

在这个异步系统中,还存在很多同步调用,例如数据收集服务同步调用异步队列的接口来发送数据(很多异步队列的客户端也提供异步发送消息的功能),这里采用同步调用,是想告诉客户端,它的数据已经安全抵达服务器,而服务器内部会保证你的数据被安全的处理,但是可能不会立即处理,如果这里也采用了异步,就会出现问题:异步传输数据出现问题时,无法告知客户端,客户端就会抱怨明明已经把数据成功交给服务器,服务器却实际上没有成功处理它;数据分析从异步队列中拉取消息也是同步调用,即数据分析服务从异步队列中拉取一条消息进行处理,处理完毕之后再从异步队列中将该消息删除;

虽然系统中存在了同步调用,但是从整个系统而言,数据收集服务和数据分析的依赖性却消失了,由于数据收集服务做的事情非常少,它能接受客户端的更多的上报请求,而数据分析服务业务复杂还需要存储数据库,它可以慢慢地从异步队列中消费消息,即便客户端上报消息的速度超过了“数据分析服务”,那么整个系统也能正常运行,从故障隔离的角度来看,一旦数据分析服务出现故障,消息会被异步队列缓存,客户端的数据上报功能依然不受影响。

从上面的例子可以看出,同步和异步在架构设计时使用要非常灵活,没有明确的方案来告诉我们什么时候应该用同步什么时候应该用异步,我们只要保证整个系统满足稳定性、扩展性等相关的要求即可。
同步转异步时,阻碍最大的地方就是调用之间产生了依赖、顺序关系,从另一个角度来看就同步操作之间产生了状态,这时如果要想将这些操作转换为异步,那就要去掉这些状态,去掉状态的办法包括:(1)将状态相关的操作整合到一个操作里,这需要调用方来实现;(2)将状态缓存到一个第三方缓存中,例如Redis、Memched等等,这需要被调用来实现;

关于正向代理和反向代理

正向代理,代理客户端发送请求,例如,我们局域网内的所有主机都不能上网,但是该局域网内有一台双网卡主机A能访问外网,这时可以在双网卡主机A上启动代理服务,在局域网内其他想访问外网的主机上配置代理服务,这样它们就可以上网了;因为这些原本无法访问外网的主机会先将上网请求发送到代理服务器A上,由代理服务A替它们访问相应的外网资源,并将请求到的网络资源返回给请求用户;对于服务端而言,它只能看到代理服务器,并不知道实际的请求来自于该局域网内的哪个主机;



正向代理的作用:

(1) 代替本机完成它本不能完成的任务,例如上面所举的上网例子;

(2) 加速访问速度,缩短响应时间,这有以下两种实现方式:

[1] 代理服务缓存常用的请求和结果,当客户端有常用请求发过来时,代理服务直接将缓存的结果返回给用户,而不用再将请求发送到服务端,从而可以提升用户访问速度,缩短访问时间;

[2] 避免低效路由,当客户端直接访问服务端要经历一个低速网段,而客户端到代理服务、代理服务到服务器端都是高速网络时,这种方法就起作用了,通常代理服务都是经过设计、优化,它访问外网的速度一般都比较快;我们经常上网时(尤其是翻墙时)配置的不同的代理,访问的速度就不一样,也是类似道理;

(3) 隐藏访问者行踪,由于代理访问代替其后面的实际用户发起网络请求,因此,请求对应服务端只能看到代理服务器,而看不到它背后的实际访问者,从而达到隐藏访问者行踪的目的。
反向代理,主要用于代理服务端接收客户端的访问请求,它是服务端设计时采用的一种技术,它能帮助服务端提升访问请求速度和系统整体扩展能力,并能隐藏业务服务器,在一定程度上提升整个系统的安全性;例如:在互联网服务端架构设计时采用的Nginx就是一个常用的反向代理服务。在反向代理服务中,所有的客户端网络请求都先发送到反向代理服务器上,而反向代理服务再将请求转交到实际的业务处理服务;对于客户端而言,它只知道反向代理服务器,并不知道反向代理服务后面的情况。



反向代理服务可以有很多工作模式:

(1) 缓存加速,反向代理服务缓存常用的客户端请求和结果,当客户端有请求过来时,首先在缓存中查找结果,如果找到则将结果直接返回给客户端,如果找不到再将请求转交到相应业务服务器;这样一方面可以加快客户端的访问速度、缩短相应时间(缓存命中时),同时还能减轻业务服务的处理压力。

(2) 负载均衡方式,反向代理服务将请求按照负载均衡算法分散到多个业务处理实例上,以减轻每个业务服务实例的压力,这时要求业务服务必须是无状态,这种方式能增加整个服务端的扩展能力

参考: http://blog.csdn.net/column/details/13917.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: