缓存穿透、缓存击穿、缓存雪崩及解决方案
本文先大致介绍缓存的两种模式:内联缓存、旁路缓存,再介绍三种缓存异常情况:穿透、击穿、雪崩的产生原因、现象及解决方案。
缓存模式
缓存分为 Inline cache 内联缓存 和 Cache-aside 旁路缓存两种模式。其中内联缓存又分为了 read through、write through 和 read/write through。
内联缓存
一般不常见,应用不直接访问存储系统,缓存系统在存储系统的前面,数据一致性由缓存系统保证。
比如要读取一个数据,如果缓存系统没有,缓存系统则去存储系统提取数据再返回。
如果要更新一个数据,应用告诉缓存要更新数据,缓存更新自己,同时更新存储系统。
旁路缓存
缓存独立于存储系统,由应用分别控制缓存和存储系统。生产环境一般都会采用。下面的三种缓存异常也基于旁路缓存讨论。
如下图所示,读缓存的场景,分为缓存命中,和缓存缺失两种情况:
缓存命中:计算系统访问缓存,数据存在,直接返回数据。
缓存缺失:计算系统访问缓存,数据不存在,从存储系统提取数据,再更新到缓存,最后返回数据。
缓存穿透
要访问的数据,在缓存和存储系统都没有。如果持续大量的访问则会同时带给缓存系统和存储系统巨大的压力。其实主要是存储系统的压力。
具体来说,查询某个 Key 的数据,缓存系统中没有数据,则直接到存储系统中查询,存储系统中也没有数据,返回空。因为是空,所以缓存系统也不会缓存这个空结果。这样每次查询这个 Key 都会经历这样的查询缓存系统,再查询存储系统的过程。
发生的原因:
业务层误操作,缓存系统中和存储系统中的数据被误删除了。
恶意攻击,专门访问不存在的数据。
解决方案:
缓存空值或缺省值,如果发生穿透,可以针对查询的数据,在缓存系统中缓存一个空值,或与业务约定的缺省值,比如商品数量约定为 0 或 -1。这样后续的访问就不需要再访问存储系统了。
布隆过滤器,在数据写入的时候,使用布隆过滤器做标记。查询时可以快速通过布隆过滤器快速判断数据是否不存在,不存在则不需要访问存储系统了。
请求入口验证,在请求入口端对业务请求数据做合法性校验。
缓存击穿
要访问的数据,在存储系统是有的,但缓存中没有。访问该数据的大量并发请求,一下子都落在了存储系统上面。
发生的原因:
一般发生在热点数据过期失效时。
也有可能发生在热点数据未初始化完成阶段。
解决方案:
设置热点数据永远不过期,如果可预测,设置合理的过期时间。
加锁,控制只有一个查询请求可以访问存储系统,其它请求被锁住,等存储系统的数据返回后更新到缓存,其它请求再读取缓存返回数据。
缓存提前预热,针对热点数据未初始化完成的情况,提前把缓存数据准备好。
缓存雪崩
要访问的数据,在存储系统是有的,但缓存中没有。大量请求在缓存系统无法处理,被发到存储系统,存储系统面临巨大的压力。
发生的原因:
缓存的大量数据同时过期。
缓存系统实例发生故障宕机。
解决方案:
对于大量数据同时过期:
避免给大量的数据设置相同的过期时间,在数据过期时间的基础上添加一定的较少的随机偏移,即满足业务需要,又不会同时过期。
服务降级,保障核心业务在缓存缺失时可以通过直接访问存储系统获取数据。
对于缓存系统实例宕机:
熔断和限流,前者为避免发生连锁反应,导致整个存储系统崩溃,暂停业务系统对缓存接口的访问。后者允许部分流量可以直接访问存储系统。
高可靠集群,比如一个主节点宕机了,从节点可以切换为主节点,继续服务。
总结
缓存穿透,是缓存和存储系统都没有数据,每个请求过来都把缓存和存储系统逐个访问一次。
缓存击穿,对于某个或某几个数据,存储系统中有,缓存本来应该有现在却没有,大量并发请求一起打过来要访问这个数据,都打在了存储系统上。
缓存雪崩,大量缓存数据同时过期失效,导致流量都打在了存储系统上。
参考资料
Redis 核心技术与实战(极客时间)
https://medium.com/@mena.meseha/3-major-problems-and-solutions-in-the-cache-world-155ecae41d4f
https://developpaper.com/cache-penetration-avalanche-hotspot-and-redis/
版权声明: 本文为 InfoQ 作者【Steven】的原创文章。
原文链接:【http://xie.infoq.cn/article/ca7e7f6d76cf60480b41f9ae4】。文章转载请联系作者。
评论