缓存命中率下降以后,先看哪些 key 没命中、数据库返回了什么,以及缓存服务是否正常。穿透、击穿、雪崩都可能增加回源,处理方法却有不同适用范围。
穿透会反复查询不存在的数据
假设商品编号格式合法,但数据库里没有对应商品。应用只缓存查到的结果,每次同样的查询都会再次访问数据库。
可以先做参数检查,再为确实不存在的结果设置短期空值缓存。数据库超时与"商品不存在"要分开,否则一次故障就可能被缓存成错误业务结论。新商品创建时,还要考虑旧空值缓存的失效。
布隆过滤器适合提前排除一部分不在集合中的编号。它的"可能存在"仍需查询,也要随数据变化维护,不能把过滤器当成精确数据库。
击穿要合并同一个热点的重建
热点缓存到期,大量请求一起发现未命中,随后查询同一份数据。需要限制同一个 key 的重复重建。
一个请求取得重建资格后,再查一次缓存,确认仍需要回源,然后读取并回填。其他请求有限等待,再读缓存或按规则降级。多个应用实例的进程内锁彼此独立,要按实际部署处理跨实例协调。
等待时间和队列也要有上限。数据库查询减少了,但所有请求都堵在锁前,应用仍可能耗尽线程或连接。允许旧数据的内容,可以考虑后台刷新;库存等时效要求高的数据,不能未经业务判断套同一策略。
雪崩还可能来自缓存服务故障
很多 key 同时到期时,适度随机 TTL 可以分散重建时间。但 Redis 整体不可用时,随机过期无法帮请求命中缓存。
此时需要限制数据库回源并发、限制排队,按业务提供降级或拒绝部分请求。缓存故障不应该自动把所有流量原样转给数据库。
Cache-Aside 官方说明介绍了应用读缓存、未命中后读取数据存储的基本关系。这个模式还需要结合过期与一致性要求设计,缓存本身不会保证数据始终最新。
验证要同时看等待与结果
分别测试不存在编号、热点到期和缓存不可用。记录数据库回源峰值、应用排队和超时、结果时效,以及缓存恢复后的重建行为。
三种情况也可能同时出现。先确认触发原因和影响范围,再选择空值缓存、重建合并或回源保护,才能判断措施是否对症。
我是程序员Sunday,相关完整解析收录在 sunday面试指南,可继续阅读《Redis 缓存穿透、击穿、雪崩有什么区别?分别怎么解决?》。