缓存穿透/击穿/雪崩
面试必考三连:穿透、击穿、雪崩。名字像,机制完全不同,先记一句话定位,再展开。
| 问题 | 一句话 | 打的是谁 |
|---|---|---|
| 穿透 | 查一个不存在的数据,缓存和库都没有 | 数据库(每次请求都穿透到库) |
| 击穿 | 一个热点 key 过期瞬间,大量请求同时打库 | 数据库(单点瞬间流量) |
| 雪崩 | 大量 key 同时过期或 Redis 挂了,流量全打库 | 数据库(整体流量) |
缓存穿透
现象:恶意或巧合的请求查不存在的 key(如负数 ID),缓存永远 miss,每次都打到数据库,可能拖垮库。
解法:
| 方案 | 做法 | 取舍 |
|---|---|---|
| 缓存空值 | 查不到也缓存,TTL 设短(几分钟) | 简单,需防大量空 key 占内存 |
| 布隆过滤器 | 请求先过过滤器,不存在直接返回 | 内存省,有误判(存在可能漏过) |
| 参数校验 | 非法参数直接拒绝 | 治标,防不住合法格式的无效 key |
缓存击穿
现象:热点 key(如秒杀商品)在过期瞬间,大量并发请求同时 miss,一起打到数据库。
解法:
- 互斥锁:第一个请求拿锁重建缓存,其他请求等待或返回旧值。锁粒度要细,避免串行化所有读
- 逻辑过期:不设物理 TTL,value 里带过期时间戳;读到逻辑过期时异步重建,请求先返回旧数据。实现复杂但无锁等待
缓存雪崩
现象:大量 key 同一时刻过期(如统一 TTL),或 Redis 实例宕机,流量瞬间全部打到数据库。
解法:
- TTL 打散:过期时间加随机值(基础值加 0 到 300 秒随机),避免整批过期
- 多级缓存:本地缓存(如 Caffeine)挡第一层,Redis 第二层,数据库兜底
- 高可用:哨兵/集群防单点宕机,宕机期间限流降级
- 限流降级:数据库侧限流,返回降级数据(如旧缓存、默认值)
对比与优先级
- 穿透和击穿、雪崩的防护可以叠加:布隆过滤器挡穿透,TTL 打散挡雪崩,互斥锁挡击穿
- 面试话术顺序:先说现象和危害,再说方案,再说取舍(空值缓存的内存代价、布隆过滤器的误判率、锁的粒度)
面试追问
- 穿透和击穿的区别? 穿透是数据不存在每次打库;击穿是热点 key 过期瞬间打库。一个是“有没有”的问题,一个是“过期时机”的问题
- 布隆过滤器有误判吗? 有,只能保证“不存在的一定不在”,存在可能误判。误判率由位数组大小和哈希函数数控制
- 互斥锁怎么实现? SETNX 拿锁重建缓存,其他请求拿不到就等或返回旧值。注意锁要设过期时间防死锁
- 逻辑过期和互斥锁怎么选? 要求强一致用互斥锁(有短暂等待),允许短暂旧数据用逻辑过期(无等待)
- 雪崩的 TTL 打散怎么做? TTL = 基础时间 + 随机偏移,让过期时刻均匀分布