06 · 缓存三大问题:击穿 / 穿透 / 雪崩
三个名字容易混,但机制完全不同:击穿是热 key 失效瞬间、穿透是查不存在的 key、雪崩是大批 key 同时失效。本篇给每个问题的触发条件、判断口诀与兜底手段。
速览卡
| 项 | 内容 |
|---|---|
| 一句话定位 | 缓存失效瞬间的三种流量尖峰:机制、口诀、兜底 |
| 核心内容 | 击穿/穿透/雪崩的区分、互斥锁、空值缓存、过期时间打散、限流降级 |
| 读完能回答 | 热点 key 失效为什么能把 DB 打挂?缓存和 DB 都没有的数据怎么防穿透? |
| 相关板块 | DB05 Redis 与缓存模式 | C13 缓存与 Redis | C21 AI 服务可靠性 |
一、一张表分清三个问题
| 问题 | 触发 | 口诀 | 受影响 |
|---|---|---|---|
| 击穿 | 一个热点 key 失效瞬间,大量并发去回源 | "一个 key 挂了" | 单个热点 |
| 穿透 | 查的 key 根本不存在,缓存永远为空,全打 DB | "查不存在的东西" | 恶意/空数据 |
| 雪崩 | 一大批 key 同时失效,一起回源 | "一批 key 同时挂" | 全局 |
记忆锚点:穿透是"从没有",击穿是"突然没了",雪崩是"一起没了"。
二、缓存击穿:热点 key 失效
机制:user:hot 没缓存时,1000 个并发请求同时发现 miss,一起打 DB。
兜底
| 手段 | 做法 |
|---|---|
| 互斥锁(Mutex) | 只有一个请求回源重建,其余等缓存回来 |
| 逻辑过期 | 值里带过期时间,读到过期就后台异步刷新,不阻塞读 |
| 热点预埋 | 大促前主动把热 key 刷进缓存并续期 |
python
# 互斥锁伪代码:只允许一个线程回源
def get_hot(key, rebuild):
val = redis.get(key)
if val:
return val
with redis.lock(f"lock:{key}", timeout=3): # 拿锁
val = redis.get(key) # 双检:别人可能已重建
if val:
return val
val = rebuild()
redis.set(key, val, ex=3600)
return val三、缓存穿透:查询不存在的数据
机制:攻击者循环请求不存在的 id,缓存永远 miss,DB 被拖垮。
兜底
| 手段 | 做法 | 注意 |
|---|---|---|
| 空值缓存 | 不存在的 key 也缓存一个"空"短 TTL(如 60s) | 有 TTL,避免缓存堆满 |
| 布隆过滤器 | 先判断"可能存在"再查 DB | 有误判率,需调参 |
| 参数校验 | 非法/超范围 id 直接拒绝 | 最简单,必做 |
| 限流 | 对异常访问模式限流 | 兜底,不解决根源 |
判断框架:先做参数校验 + 空值缓存(成本最低);布隆过滤器是"查询量真的很大"时的进阶手段。
四、缓存雪崩:一大批 key 同时失效
机制:缓存里上万 key 同一秒过期,全部回源,DB 瞬间被压垮。
兜底
| 手段 | 做法 |
|---|---|
| 过期时间打散 | 基础 TTL + 随机偏移(如 3600 + rand(0,600)) |
| 多级缓存 | 本地缓存(进程内)挡第一波,Redis 挡第二波 |
| 限流 + 降级 | 回源时对 DB 侧限流,超过阈值返回降级数据 |
| 缓存集群高可用 | 避免"Redis 宕机"这种最极端雪崩 |
雪崩的另一大来源是 Redis 本身重启/故障——这要求缓存有高可用(主从/集群),不只是调 TTL。
五、统一兜底清单
| 层 | 手段 |
|---|---|
| 缓存前 | 参数校验、布隆过滤器(防穿透) |
| 缓存中 | 热 key 逻辑过期、互斥锁(防击穿) |
| 缓存 TTL | 加随机偏移(防雪崩) |
| 缓存外 | 本地二级缓存、限流降级、Redis 高可用 |
| 长期 | 监控回源率与 DB 负载,故障前发现尖峰 |
六、自检清单
- [ ] 能区分击穿/穿透/雪崩的触发条件
- [ ] 击穿用互斥锁或逻辑过期,避免热点失效打挂 DB
- [ ] 穿透用参数校验 + 空值缓存(必要时布隆过滤器)
- [ ] 雪崩用过期时间打散 + 多级缓存 + 限流
- [ ] 知道 Redis 高可用也是防雪崩的一环
- [ ] 有回源率监控,能提前发现尖峰
七、常见误区
| 误区 | 事实 |
|---|---|
| "击穿=穿透=雪崩" | 机制完全不同,针对手段也不同 |
| "设短 TTL 就防雪崩" | 同批同时过期才雪崩,要打散而非单纯缩短 |
| "空值缓存占空间" | 设短 TTL,成本可接受 |
| "布隆过滤器零误差" | 有误判率,要按数据量调位数组与哈希数 |
| "互斥锁万能" | 锁内回源仍有一次 DB 压力,逻辑过期适合高并发热 key |
| "缓存挂了无所谓" | Redis 宕机就是最大雪崩,要主从/集群高可用 |
八、动手练习
练习 A:识别场景
给出三个现象,判断分别是击穿/穿透/雪崩,并给对应兜底:
- 秒杀商品详情在 0 点失效后 DB 卡死
- 攻击者用随机 id 循环请求
- 凌晨缓存统一 4 点过期,DB 同时飙高
练习 B:写互斥锁
用你熟悉的语言给"热点数据获取"加互斥锁 + 双检(double-check),并说明为什么需要双检。
练习 C:给 TTL 加抖动
写一个"过期时间随机化"的封装:set(key, val, base_ttl),实际 TTL 在 base_ttl 到 base_ttl * 1.2 之间随机。
相关文档
本区
- 05 · Redis 与缓存模式 —— 缓存链路怎么搭,这里补失效瞬间的坑
- 08 · 分库分表与读写分离 —— 大流量下的下一步
其他域
- 13 · 缓存与 Redis —— 命令与过期策略
- 07 · 服务端性能与容量 —— 限流与容量规划
- 21 · AI 服务可靠性 —— 限流/降级在 AI 服务的应用