什么是缓存雪崩?如何解决?
简化版
缓存雪崩指大量缓存 key 在同一时间集中失效,或缓存服务整个宕机,导致海量请求瞬间全部落到数据库,把 DB 压垮、甚至引发整个系统崩溃(连锁反应像雪崩)。它和击穿的区别是「击穿是单个热点 key,雪崩是大批 key 同时出事」。解法分两类:针对「集中过期」——给过期时间加随机值打散,避免同时失效;针对「缓存宕机」——搭 Redis 高可用集群 + 多级缓存 + 服务熔断降级限流兜底。
详细版
雪崩的两种成因:
- 大量 key 同时过期:比如系统启动时批量加载缓存、都设了相同的过期时间(如统一 3600s),一小时后齐刷刷失效。
- 缓存服务宕机:Redis 整个挂了,所有请求瞬间失去缓存保护,直接压向 DB。
解法(对应两种成因):
// 成因一解法:过期时间加随机值,打散失效时间点
int baseExpire = 3600;
int randomExpire = baseExpire + new Random().nextInt(600); // 3600~4200s 随机
redis.set(key, value, randomExpire);
- 过期时间打散:基础过期时间 + 随机偏移,避免大量 key 同一秒失效。
- 热点数据永不过期:核心热点后台定时刷新,不设 TTL。
- Redis 高可用:主从 + 哨兵 / Cluster,避免单点宕机导致缓存全失效。
- 多级缓存:本地缓存(Caffeine)+ 分布式缓存(Redis),Redis 挂了本地还能挡一层。
- 服务降级 / 熔断 / 限流:DB 扛不住时,用 Sentinel/Hystrix 限流降级,保护 DB 不被打死。
完整版教学
一、雪崩的画面:大批 key 同时失效,DB 瞬间被压垮
想象一个电商系统,凌晨批量把 10 万个商品缓存加载进 Redis,都设了统一的 1 小时过期。一小时后的那一秒,这 10 万个 key 同时失效。此刻如果有大量用户在访问,这些请求全部未命中缓存、一股脑压向数据库——数据库瞬间承受平时几十倍的压力,响应变慢、连接耗尽,进而拖垮上游服务,引发连锁崩溃。这就是「雪崩」名字的由来:一处失效,层层压垮。
另一种雪崩是缓存服务本身宕机:Redis 集群挂了,所有 key 一瞬间全部「消失」,效果比集中过期更惨烈——全部请求都失去缓存保护。
二、成因一「集中过期」的解法:打散过期时间
雪崩的核心是「同时失效」,那解法就是让失效时间错开、分散:
① 过期时间加随机值。 不要给所有 key 设同一个固定过期时间,而是在基础时间上加一个随机偏移,比如 3600 + random(0, 600) 秒。这样 key 的失效时间点被均匀打散在一段区间里,不会在同一秒集中失效,DB 压力被摊平。这是最简单有效的一招。
② 热点数据永不过期 + 定时刷新。 对核心热点数据,干脆不设 TTL,由后台任务定时更新,从根上避免过期。
三、成因二「缓存宕机」的解法:高可用 + 多级 + 兜底
如果是 Redis 整体挂了,打散过期时间没用了,要靠架构层面的防护:
① Redis 高可用集群。 单机 Redis 是单点,一挂全完。要用主从复制 + 哨兵(Sentinel) 或 Redis Cluster,主节点宕机自动故障转移到从节点,保证缓存服务本身不轻易全挂。这是防雪崩的地基。
② 多级缓存。 在 Redis(分布式缓存)之上再加一层本地缓存(如 Caffeine、Guava Cache,在应用 JVM 内)。请求先查本地缓存 → 未命中查 Redis → 再未命中查 DB。即使 Redis 挂了,本地缓存还能挡住一部分请求,给系统争取喘息空间。代价是本地缓存的一致性更难保证(每台机器一份)。
③ 服务降级、熔断、限流。 这是最后的兜底防线——保 DB 不被打死。用 Sentinel/Hystrix/Resilience4j:
- 限流:限制打到 DB 的请求速率,超过阈值的请求快速失败或排队。
- 熔断:检测到 DB 响应变慢/出错率高时,直接熔断,短时间内不再请求 DB。
- 降级:返回兜底数据(默认值、缓存旧值、友好提示),保证系统不彻底崩溃。
宁可让部分请求失败/返回降级结果,也不能让数据库被打死导致全站瘫痪。
四、雪崩、击穿、穿透的统一对照
| 数据是否存在 | 触发点 | 核心解法 | |
|---|---|---|---|
| 穿透 | 不存在 | 查不存在的数据 | 空值缓存、布隆过滤器 |
| 击穿 | 存在 | 单个热点 key 失效瞬间 | 互斥锁、逻辑过期 |
| 雪崩 | 存在 | 大量 key 同时失效 / 缓存宕机 | 过期打散、高可用、多级缓存、限流降级 |
面试常连着问这三个,记住它们的触发规模和成因是区分关键:穿透看「不存在」,击穿看「一个热点失效」,雪崩看「一大批同时失效」。
五、实践中的组合拳
生产环境防雪崩不是单一手段,而是层层设防:
- 过期时间加随机(防集中过期);
- Redis 主从 + 哨兵/Cluster 高可用(防单点宕机);
- 本地缓存 + Redis 多级缓存(Redis 挂了兜一层);
- 限流 + 熔断 + 降级(保护 DB 的最后防线);
- 缓存预热(系统上线/重启前先把热点数据加载好,避免冷启动时大量 miss)。
六、常见误区与追问
| 考点 | 正确口径 |
|---|---|
| 缓存雪崩 | 大量 key 同时失效或缓存集群故障,流量压到数据库 |
| 核心危害 | 数据库瞬时流量暴涨,可能引发级联故障 |
| 治理思路 | 过期时间打散、多级缓存、限流降级、预热和熔断 |
100000 hot keys expire at 12:00:00
each key gets 100 QPS
database may receive 10,000,000 QPS suddenly
random TTL: base 3600s + random(0..600s)
雪崩关注的是“很多 key 一起出事”,不是某一个热点 key 被打爆。
- 误区:缓存雪崩就是某个热点 key 失效。 单个热点 key 失效更接近缓存击穿,雪崩强调大面积 key 同时失效或缓存整体不可用。
- 误区:只要把 TTL 设长就能解决雪崩。 TTL 设长会降低失效频率,但仍可能在批量发布、缓存故障或集中预热后同时过期。
- 误区:雪崩只靠 Redis 高可用就够。 Redis 高可用能降低集群故障概率,但还要做限流、降级、本地缓存和数据库保护。
- 追问:为什么 TTL 要加随机值? 随机扰动能把过期时间打散,避免同一时间大量 key 同时回源。
- 追问:缓存预热有什么作用? 系统上线或大促前提前加载热点数据,避免冷启动时全部请求打到数据库。
- 追问:数据库保护怎么做? 可用请求限流、熔断降级、只返回兜底数据、队列削峰和热点本地缓存。
七、加强记忆
缓存雪崩 = 大量 key 同时失效 或 缓存服务整体宕机,海量请求瞬间压垮 DB(连锁崩溃)。区别于击穿(单个热点 key 失效)、穿透(数据不存在)。解法按成因分两类:「集中过期」→ 过期时间加随机值打散 + 热点永不过期;「缓存宕机」→ Redis 主从哨兵/Cluster 高可用 + 本地+Redis 多级缓存 + 限流熔断降级兜底,再配合缓存预热。三兄弟区分口诀:穿透查不存在、击穿一个热点失效、雪崩一大批同时失效。核心防线简明区分:打散过期防集中、高可用防宕机、限流降级保 DB。