缓存穿透、击穿、雪崩:三个词分不清,方案就会用错
这三个词都在说"缓存扛不住,请求打到 DB 了",但成因完全不同,方案也不能互换。 面试里最常被扣分的答案,是把三者混成一句"加锁 + 布隆过滤器 + 过期时间打散"。
1. 一张表先分清
| 名词 | 触发条件 | 请求特征 | 关键区别 |
|---|---|---|---|
| 穿透 | 查的数据根本不存在 | 大量请求 key 不存在,缓存永远不命中 | 请求"绕过"了缓存 |
| 击穿 | 某个热点 key 过期 | 单个 key 的并发瞬间打到 DB | 单点失效 |
| 雪崩 | 大量 key 同时过期 或缓存整体不可用 | 全站请求同时打到 DB | 面失效 |
2. 穿透:请求的数据不存在
请求 user_id=99999999(不存在)
→ 缓存未命中
→ 查 DB,查不到
→ 不写缓存(没数据可写)
→ 下一个同样的请求,重复上面
攻击者可以用不存在的 id 打穿缓存直击 DB——这既是性能问题也是安全问题。
方案
- 缓存空值:查不到也写一个短 TTL 的空标记(如 60s)
优点:实现最简;缺点:恶意构造大量随机 key 会撑爆缓存(要配合限流)。key=user:99999999 value="" ttl=60s - 布隆过滤器:写入数据时把 key 放进布隆过滤器,查询前先判断"可能存在"
注意:只能判断"一定不存在",不能判断"一定存在";删除需要计数布隆或定期重建。布隆说不存在 → 直接返回(100% 准确) 布隆说存在 → 再查缓存/DB(可能误判) - 参数校验 + 限流:id 范围、格式校验,前置网关限流——最简单也最容易被忽略。
3. 击穿:热点 key 过期
某个 key(比如首页 banner、秒杀商品)QPS 极高,一到过期瞬间, 所有并发请求同时 miss → 同时查 DB。
方案
- 互斥锁(只让一个线程回源)
注意:锁的粒度按 key,不要用一把全局锁。String v = cache.get(key); if (v == null) { if (lock.tryLock()) { // 只放一个请求去查 DB try { v = db.query(key); cache.set(key, v, ttl); } finally { lock.unlock(); } } else { Thread.sleep(50); // 其它请求稍等再读缓存 return cache.get(key); } } - 逻辑过期(不设 TTL,值里带过期时间)
优点:永不阻塞读;缺点:会短暂返回旧数据(对一致性要求低的场景最合适)。读取时判断 value.expireAt 是否过期 未过期 → 直接返回 已过期 → 返回旧值,同时异步起一个线程刷新 - 热点 key 永不过期 + 主动更新:适合配置类、banner 类数据。
4. 雪崩:大面积同时失效
两种成因:
- 同一时刻批量写入的 key 用了相同 TTL → 到期时间被对齐;
- Redis 实例宕机/网络分区 → 缓存整体不可用。
方案
- TTL 加随机抖动
把"同时过期"打散成"陆续过期"。int ttl = baseTtl + ThreadLocalRandom.current().nextInt(0, 300); - 缓存高可用:主从 + 哨兵 / Cluster,避免单点;多级缓存(本地 Caffeine + Redis)。
- 限流 + 降级:缓存挂了时,网关限流保护 DB,非核心功能直接降级返回兜底数据。
- 预热:重启/发布后先加载热点数据,避免冷启动瞬间全穿透。
5. 三者可以叠加出现
真实故障往往是组合:热点 key 过期(击穿)→ 大量请求查 DB → DB 变慢 → 更多 key 超时未写缓存 → 雪崩。
所以完整方案是一套组合拳:
接入层:参数校验 + 限流(防穿透的第一道闸)
缓存层:空值缓存 + 布隆过滤器(防穿透)
互斥锁 / 逻辑过期(防击穿)
TTL 抖动 + 高可用 + 预热(防雪崩)
兜底层:降级预案 + DB 连接池保护(防雪崩的最后一环)
6. 常被追问的两个点
Q:为什么不用"永不过期"解决一切? 内存有限,且数据会变;永不过期意味着你要自己维护更新逻辑,复杂度转移到业务侧。
Q:布隆过滤器能删除元素吗? 标准布隆不能(删除会破坏其他元素的判定)。要删除用计数布隆(Counting Bloom), 或者用"定期重建 + 双缓冲"切换。
7. 一个小结口诀
- 穿透:查不存在 → 空值缓存 / 布隆 / 校验;
- 击穿:热点过期 → 互斥回源 / 逻辑过期;
- 雪崩:大面积失效 → TTL 抖动 / 高可用 / 限流降级。
面试时先讲清"三者成因不同",再分别给方案,最后补一句"真实故障常是组合,所以要做组合防御"—— 这比背三个名词更能体现你处理过线上问题。