Redis 缓存穿透、击穿、雪崩:先分清三者再谈方案
缓存穿透、击穿、雪崩,是用 Redis 做缓存时最常被放在一起讨论的三个问题。它们都会让请求绕过缓存打到数据库,表现都是数据库压力骤增,但成因完全不同:一个是“查了不存在的东西”,一个是“热点 Key 恰好过期”,一个是“大量 Key 一起失效”。分不清三者就谈方案,很容易药不对症——给击穿问题加布隆过滤器,或者给雪崩问题只加一台数据库,都治标不治本。
一、先看一张对照表
| 问题 | 触发条件 | 本质 | 影响面 |
|---|---|---|---|
| 缓存穿透 | 请求的数据在缓存和数据库中都不存在 | 每次请求都回源,缓存形同虚设 | 持续、分散的无效请求 |
| 缓存击穿 | 某个热点 Key 过期的一瞬间,大量并发请求同时到达 | 单个 Key 的并发回源 | 集中在某一个热点 Key 上 |
| 缓存雪崩 | 大量 Key 在同一时段集中过期,或缓存服务整体不可用 | 大面积回源 | 短时间内全量请求压向数据库 |
记住这张表,后面所有方案都是从“触发条件”出发的。
二、缓存穿透:查了不存在的东西
典型场景:请求一个不存在的用户 ID 或商品 ID。缓存里没有,数据库里也没有,每一次请求都要查一次数据库。如果这类请求量很大,或者是被刻意构造的,就会持续消耗数据库资源。
常用做法有两类。第一类是缓存空值:数据库查不到时,也在缓存里写一个空结果,并设置一个较短的过期时间。这样重复的无效请求会被缓存挡住。空值的过期时间不宜过长,否则数据真正写入后,读到的仍是旧的空结果。第二类是在缓存之前加一层布隆过滤器,先判断 Key 是否可能存在,不存在就直接返回,不再访问缓存和数据库。布隆过滤器存在一定的误判可能,设计时要结合业务对误判的容忍度来评估。
三、缓存击穿:热点 Key 恰好过期
击穿和穿透只差一个字,成因却完全不同:击穿针对的是真实存在、且访问量很大的热点 Key。当这个 Key 的过期时间一到,瞬间涌入的大量并发请求都会发现缓存失效,全部打到数据库上重建缓存。
常用做法也有几类。第一类是热点 Key 不设置固定过期时间,改用逻辑过期:缓存中附带一个过期时间字段,读到逻辑过期的数据时,由后台异步刷新,读请求仍返回旧值,适用于允许短暂读到旧数据的场景。第二类是互斥锁,也就是常说的“单飞”:只有一个请求能去数据库重建缓存,其他请求等待或直接返回旧值。第三类是提前预热和主动刷新:在热点 Key 临近过期前就主动更新,避免在高峰时刻集中过期。
四、缓存雪崩:大量 Key 一起失效
雪崩的破坏力最大。一种情况是大量 Key 在业务高峰期同时过期——比如一批缓存当初用了相同的过期时间;另一种情况是缓存服务本身不可用,所有请求只能走数据库。
应对思路是“分散风险 + 保护数据库”。过期时间不要设成同一个值,可以在基础过期时间上加一个随机值打散;缓存层做高可用,避免单点故障;数据库一侧做限流和熔断,请求超过阈值时快速失败或返回降级结果,而不是任由连接被打满;有条件时还可以引入多级缓存,让本地缓存先挡住一部分请求。
五、组合起来怎么选
实际系统中,三种问题可能同时存在,方案也常常组合使用:
- 以读多、Key 空间相对固定的业务为主:布隆过滤器加空值缓存,先挡住穿透。
- 有明显热点数据的业务:热点 Key 重点保护,用逻辑过期或互斥锁,避免击穿。
- 缓存规模大、Key 数量多的业务:过期时间随机化是性价比很高的基础工作。
- 核心链路:再叠加限流、熔断和降级,保证最坏情况下数据库不被打垮。
选型时先问自己三个问题:请求的数据存在吗?有没有热点 Key?Key 的过期时间分布均匀吗?三个答案就是三类方案的优先级。
六、三个常见误区
第一,把击穿当成穿透治。热点 Key 是真实存在的,布隆过滤器拦不住它,空值缓存也帮不上忙。第二,空值缓存的过期时间设得过长,导致新写入的数据长时间读不到。第三,雪崩发生时只想着给数据库加机器,却不处理过期时间集中的根因,下一次高峰还会重演。
小结
穿透防“不存在”,击穿防“热点过期”,雪崩防“集中失效”。先把三者的触发条件分清,再按场景组合方案,比上来就堆一堆手段更有效。缓存方案没有银弹,真正起作用的往往是过期策略、热点识别和降级保护这几件基础工作。
- 点赞
- 收藏
- 关注作者
评论(0)