前言
先要明白为什么要使用缓存,这个和缓存的目的/收益有关,缓存可以减少服务端压力、提高响应处理量、缩短响应时间等。如果本身的方案在未来3-5年内足以支撑业务需求,是否需要缓存可以再进行斟酌。现在技术更新很快,可能2-3年后会有更好的通用解决方案。
考虑点
成本和收益
- 成本
- 中间需要加一层缓存,需要增加硬件成本以及软件的复杂度
- 缓存数据和DB中数据由于时差问题,需要考虑数据不一致性问题。能否接受数据的不一致性,或能容忍多长时间的不一致,需要依据数据特性以及业务来决定
- 收益
- 减少服务端压力,提高响应处理量,缩短响应时间
原则
- 离最终的计算结果越近,越有价值。即数据尽可能保证一致
- 对于需要消耗I/O资源的缓存,要尽可能的减少调用的次数
缓存颗粒度
- 缓存整个对象,还是缓存部分信息。这个需要根据业务使用场景来识别。
- 缓存整个对象,内存占用高,可以考虑通用的解决方案。如key不存在时,统一访问DB (select * from table),将整个结果集缓存。
- 缓存部分信息,内存占用低,但需要根据业务来识别。
如热点新闻展示,标题的获取是高频的,但内容的获取相对而言是低频的(因为大家只对自己感兴趣的新闻会点击进去查看)。这种情况下,建议缓存标题等关键展示信息
更新策略
- 设置过期时间更新
- 实现简单,短期内可能会存在数据不一致的问题
- 数据永不过期,由程序主动来更新
- 增加了实现/维护的复杂度,数据准实时一致性
问题与优化
缓存穿透
问题描述
某个key,从缓存中找不到后会再次请求DB。如果恶意构造大量不存在的key来访问,比单独访问DB的开销会更大,对服务器造成巨大的压力
方案
从缓存本身考虑
- 如果通过key去DB中找不到数据,亦加入缓存,对值进行标记(如null、-1等),后续从缓存中取到该key的信息时,直接返回
- 缓存不过期。即认为缓存是可信、准确的,如果没有取到,即返回。此时需要程序实现来保证,如果数据有变动,能够来主动或定时刷新缓存
其他方面考虑
- 从整个部署的角度来考虑恶意攻击的问题,如黑白名单控制,访问量控制,token拦截、签名、https等
缓存雪崩
问题描述
缓存的数据,到了某个特定的时间点,所有的数据均失效,导致该时间点接收到的所有请求均需要访问DB来刷新缓存
方案
- 缓存数据的过期时间错开,如在指定的过期时间上增加随机数,如在[-100s,100s]内随机。可接受的时间依据具体业务确定
- 缓存不过期,使用程序来主动刷新缓存
缓存击穿
问题描述
- 某个key的缓存过期后,同一时间内有大量的请求均访问该key,由于缓存过期,大量的请求均会访问DB,重建缓存
方案
-
重建缓存的过程加锁,保证只有一个人执行,其他人等待
-
缓存不过期
tip: 文中提到的DB,是数据源的统称,可以是数据库,亦可以是其他的数据源,如文件、第三方数据提供接口等。
参考资料
- https://blog.youkuaiyun.com/zeb_perfect/article/details/54135506 缓存穿透,缓存击穿,缓存雪崩解决方案分析
- https://blog.youkuaiyun.com/u010632868/article/details/52295554. 高效使用缓存的原则
- http://gitbook.cn/gitchat/column/5a55d8e232c7126d8482f5d2. GitChat课程,Redis 入门到分布式实践中第9课,缓存设计与优化.