越过山丘,才发现无人等候。--- 李宗盛《山丘》
在大型网站开发中,缓存是个永远避免不了的话题,也不存在一种方案能解决所有的问题。缓存可以说是无处不在,比如 PC 电脑中的内存、CPU 中的二级缓存、HTTP 协议中的缓存控制、CDN 加速技术都是使用了缓存的思想来解决性能问题。缓存是用于解决高并发场景下系统的性能及稳定性问题的主要手段。
一、缓存开发过程经常碰到的问题:
过期策略(惰性)
缓存更新(独立)
多级缓存
分布式缓存(分片)
高可用(单点)
高并发(雪崩)
命中率(穿透)
缓存淘汰(LRU)
...........
多级缓存方案的层级关系大都是由浏览器->cdn->反向代理缓存->线程级->内存级->进程级->文件(静态资源)->分布式(redis)->Db数据库
结果。
二、缓存管理
1、拿来主义机制
拿来主交机制,指的是当有多个用户请求同一个数据时,会选举出一个 leader 去数据源加载数据,其它用户则等待其拿到的数据。并由 leader 将数据写入缓存。
2、自动加载机制
自动加载机制,将用户请求及缓存时间等信息放到一个队列中,后台使用线程池定期扫这个队列,发现缓存即将过期,则去数据源加载最新的数据放到缓存中,达到将数据长驻内存的效果。从而将这些数据的请求,全部引向了缓存,而不会回到数据源去获取数据。这非常适合用于缓存使用非常频繁的数据,以及非常耗时的数据
。
为了防止自动加载队列过大,设置了容量限制;同时会将超过一定时间没有用户请求的数据从自动加载队列中移除,把服务器资源释放出来,给真正需要的请求。
往缓存里写数据的性能相比读的性能差非常多,通过上面两种机制,可以减少写缓存的并发,提升缓存服务能力。
三、缓存使用建议
1、读写小于10:1的情况下,不适合用缓存,我们用缓存的目的就是想分摊下数据库的压力以及利用内存来提速性能,如果读写差不多,或者压根就没读过,这样的死数据就会造成内存资源的浪费。
2、既然是缓存,就注定了它的资源是有限的,宝贵的,也就注定了我们必须合理利用它的内存空间,也就被迫的让我们清楚的认识到热点数据
,不易修改的应该放在缓存,反之不宜放。
3、大公司在缓存方面做的好的地方就是在一个“控”字上,他们会为缓存专门做一套“缓存系统”,当系统预加载的时候,同时也充当内存数据库使用,将这些元数据加载到缓存系统中,比如“县市区”,“分类信息”等等作为预热数据。
四、缓存设计
缓存设计需要解决以下几个问题:
1、缓存什么?
哪些数据需要缓存:1.热点数据;2.静态资源;
2、 缓存的位置?
CDN,反向代理,分布式缓存服务器,本机(内存,硬盘)
3、如何缓存的问题?
3.1、过期策略
1.固定时间:比如指定缓存的时间是30分钟;
2.相对时间:比如最近10分钟内没有访问的数据;
3.2、同步机制
实时写入;(推)
异步刷新;(推拉)
3.3、数据一致性
缓存是在数据持久化之前的一个节点,主要是将热点数据放到离用户最近或访问速度更快的介质中,加快数据的访问,减小响应时间。
因为缓存属于持久化数据的一个副本,因此不可避免的会出现数据不一致问题。导致脏读或读不到数据的情况。数据不一致,一般是因为网络不稳定或节点故障导致。根据数据的操作顺序,主要有以下几种情况。
3.3.1、先写缓存,再写数据库
假如缓存写成功,但写数据库失败或响应延迟,则下次读取(并发读)缓存时,就出现脏读;
3.3.2、先写数据库,再写缓存
假如写数据库成功,但写缓存失败,则下次读取(并发读)缓存时,则读不到数据;
3.3.3、缓存异步刷新
指数据库操作和写缓存不在一个操作步骤中,比如在分布式场景下,无法做到同时写缓存或需要异步刷新(补救措施)时候。
此种情况,主要考虑数据写入和缓存刷新的时效性。比如多久内刷新缓存,不影响用户对数据的访问。
3.3.4、数据不一致性解决方法
第一个场景3.3.1:
这个写缓存的方式,本身就是错误的,需要改为先写持久化介质,再写缓存的方式。
第二个场景3.3.2:
1、根据写入缓存的响应来进行判断,如果缓存写入失败,则回滚数据库操作;此种方法增加了程序的复杂度,不建议采用;
2、缓存使用时,假如读缓存失败,先读数据库,再回写缓存的方式实现。
第三个场景3.3.3:
1、首先确定,哪些数据适合此类场景;
2、根据经验值确定合理的数据不一致时间,用户数据刷新的时间间隔;
其他方法
1、超时:设置合理的超时时间;
2、刷新:定时刷新一定范围内(根据时间,版本号)的数据;
3、以上是简化数据读写场景,实际中会分为:(1)缓存与数据库之间的一致性;
(2)多级缓存之前的一致性;
(3)缓存副本之前的一致性。
4、缓存高可用
业界有两种理论,
第一套缓存就是缓存,临时存储数据的,不需要高可用。
第二种缓存逐步演化为重要的存储介质,需要做高可用。
4.1、具体的决策依据需要根据,集群的规模(数据,缓存),成本(服务器,运维),系统性能(并发量,吞吐量,响应时间)等方面综合评价。
4.2、缓存的高可用,一般通过分布式和复制实现。
4.3、分布式实现数据的海量缓存,复制实现缓存数据节点的高可用。
4.4、分布式采用一致性Hash算法,复制采用异步复制。
五、分布式缓存
一般情况下,会有两种形式,第一种就是主从复制
的模式,第二种就是分片
的模式。
1、主从复制模式
这种模式就是一份内存,多处备份,当其中某一个缓存内容中的数据有变化时,会及时通知其他机器进行缓存更新或清除。
缺点
、在于比较容易受制于单台机器的内存限制
优点
、在于用心跳机制及时用另一台缓存机器顶替,否则当机器内存爆满的时候就比较尴尬了。
2、分片的模式
这种模式在大型网站中还是被大量使用的,它的特点就是可以把一大坨数据通过一定的算法和配置分摊到集群中的若干台机器上,如果集群中的某一台机器挂了,没关系,只会影响到该台机器中的数据,对数据库不会造成很大的影响。
一个典型的应用就是memcache,memcache是一个非常简单,实用,高效的分布式缓存架构,其实memcache最值得一提的就是“路由算法的一致性hash”技术使得我们的memcache集群可以自由伸缩,不过现在已经有很多的nosql产品,比如redis,couchdb,mongodb等等。
六、缓存架构一般示例
1、缓存架构示例图如文章开头
,其职责划分:
1、CDN:存放HTML,CSS,JS等静态资源;
2、反向代理:动静分离,只缓存用户请求的静态资源;
3、分布式缓存:缓存数据库中的热点数据;
4、本地缓存:缓存应用字典等常用数据;
2、请求过程:
1、浏览器向客户端发起请求,如果CDN有缓存则直接返回;
2、如果CDN无缓存,则访问反向代理服务器;
3、如果反向代理服务器有缓存则直接返回;
4、如果反向代理服务器无缓存或动态请求,则访问应用服务器;
5、应用服务器访问本地缓存;如果有缓存,则返回代理服务器,并缓存数据;(动态请求不缓存)
6、如果本地缓存无数据,则读取分布式缓存;并返回应用服务器;应用服务器将数据缓存到本地缓存(部分);
7、如果分布式缓存无数据,则应用程序读取数据库数据,并放入分布式缓存
七、参考
1、面对缓存,有哪些问题需要思考?
2、我也要谈谈大型网站架构之系列(3)——死了都要说的缓存
3、大型网站架构系列:缓存在分布式系统中的应用(一)
4、大型网站架构系列:缓存在分布式系统中的应用(二)
5、大型网站架构系列:缓存在分布式系统中的应用(三)