★ 缓存穿透、缓存击穿、缓存雪崩分别是什么?如何应对?
结论先行:三个问题都是“缓存缺失/失效导致请求直达数据库”的形态差异——穿透是查询根本不存在的 key,永远绕过缓存打库(常见于恶意扫描);击穿是某个热点 key 在过期瞬间被大量并发同时回源;雪崩是大量 key 同时失效或缓存整体宕机,流量洪峰直接压垮数据库。对策主线:穿透用“空值缓存 + 布隆过滤器 + 入参校验”;击穿用“互斥重建 / 逻辑过期”;雪崩用“过期时间加随机抖动 + 缓存高可用 + 限流降级兜底”。
三兄弟对比表
| 问题 | 表象 | 典型场景 | 核心对策 |
|---|---|---|---|
| 穿透 | 永远查不到 | 扫描不存在的 id | 空值缓存、布隆过滤器、入参校验 |
| 击穿 | 单个热点 key 过期 | 爆款商品/秒杀详情 | 互斥重建、逻辑过期、永不过期+异步刷新 |
| 雪崩 | 大批 key 同时失效/宕机 | 缓存集体到期、节点故障 | 过期加随机值、集群高可用、限流降级 |
防击穿:互斥重建(伪代码)
// 热点 key 过期瞬间,只允许一个请求回源重建缓存
Object v = cache.get(key);
if (v == null) {
synchronized (lock(key)) { // 单机用 synchronized;多机改用 Redis SETNX
v = cache.get(key); // 双重检查,避免重复回源
if (v == null) {
v = db.query(key);
cache.set(key, v, ttl + random()); // 过期时间加随机值,兼防雪崩
}
}
}
兜底与降级(多一层防护)
- 即使做了互斥重建,回源流量仍可能打爆 DB → 对热点 key 单独限流;
- 缓存集群整体故障瞬间 → 进程内本地缓存(多级缓存)先顶上;
- 数据库侧保留降级开关:极端情况下直接返回降级文案或默认数据;
- 监控三个指标:穿透量、回源重建量、成批失效 key 数,报警才有依据。
布隆过滤器示意(伪代码)
// 布隆过滤器可拦截“一定不存在”的 key,DB 零压力
if (!bloom.mightContain(key)) {
return null; // 一定不存在,直接返回
}
Object v = cache.get(key); // 可能存在 → 走缓存再回源
常见追问 / 记忆点
- 追问:布隆过滤器为什么能防穿透?答:把存在的 key 哈希到位图,查询时不在位图直接返回,能拦下绝大多数不存在的 key;代价是“存在”可能误判。
- 追问:缓存不设过期时间可行吗?答:可配合“逻辑过期 + 异步重建”,但要接受短暂不一致窗口,并控制内存占用。
- 记忆点:穿透查“空”、击穿挂“热点”、雪崩炸“一片”;对策口诀——空值/布隆、互斥/逻辑过期、随机抖动/多级缓存。