996 字
约 3 分钟
7
S12-J01 · 缓存穿透、击穿、雪崩

适用读者:分不清这三个词、或只会背定义的人。
前置:S12-B05。
覆盖旧文:879(110) 2671(108) 1840(79) = 297 点。
上一篇:S12-B05 · 缓存入门与 Spring Boot 整合
下一篇:S12-J02 · 缓存与数据库一致性。


1. 一句话结论

三个词的区别只在"缓存为什么没挡住":穿透=查的根本不存在;击穿=热点 key 到期;雪崩=大量 key 同时到期。

问题 缓存状态 请求特征 后果
穿透 缓存和 DB 都没有 恶意/无效 key(如 id=-1) 每次都打 DB
击穿 单个热点 key 过期 集中访问同一 key DB 瞬间被打爆
雪崩 大量 key 同时过期 / Redis 宕机 全量请求 DB 全面过载

2. 穿透:为什么"查不到"也是攻击面

请求 id=-1 → 缓存 miss → 查 DB(没有)→ 不写缓存 → 下次再来一遍
方案 说明 代价
缓存空值 查不到也写 null(短 TTL) 占用内存,需防大量随机 key
布隆过滤器 前置判断"可能存在吗" 有假阳性、不能删除
参数校验 非法 id 直接拒 只能挡低级攻击

判据:"无效 key 会不会大量且随机?" 会 → 布隆过滤器;少量 → 缓存空值。


3. 击穿:热点 key 到期那一刻

方案 说明
互斥锁重建 只让一个请求查 DB,其他等待
逻辑过期 不设 TTL,value 里带过期时间,异步刷新
热点永不过期 + 后台刷新 最简单可靠

"互斥锁"的标准写法:

GET cache → miss → SET lock NX PX 3000 成功者查 DB 并回填 → 释放锁
                            ↓ 失败者
                       短暂 sleep 后重试 GET

注意:这把锁必须是"尽力而为"——锁超时要能自愈(J03 的锁问题同源)。


4. 雪崩:从"同时过期"到"Redis 挂了"

原因 方案
大量 key 同一时刻过期 TTL 加随机抖动
Redis 实例挂掉 高可用(哨兵/Cluster,A01)+ 本地缓存兜底
冷启动 预热

"TTL 抖动"的具体做法:

long ttl = base + ThreadLocalRandom.current().nextLong(0, jitter);

判据:"这批 key 是不是同一批导入、同一个 TTL?" 是 → 必然同时过期。


5. 三者共用的兜底

兜底 说明
限流/熔断 保护 DB(S17)
本地缓存 Redis 挂了还能挡一层
降级 返回默认值/静态页

"缓存问题的最终防线不是缓存,是 DB 保护"——限流与熔断永远要在。


6. 五个误区

误区 真相
"三个问题是一回事" 触发条件完全不同
"缓存空值就能防穿透" 大量随机 key 会撑爆内存
"热点 key 设长 TTL 就行" 到期瞬间仍会击穿
"TTL 都一样比较整齐" 整齐 = 雪崩
"有了缓存就不怕 Redis 挂" 需要本地缓存/降级

7. 自检

  1. 三者的触发条件各是什么?
  2. 穿透的三种方案与代价?
  3. 击穿的"逻辑过期"怎么做?
  4. 雪崩的两个根因?
  5. 为什么说"最终防线是 DB 保护"?

8. 下一篇

S12-J02 · 缓存与数据库一致性

三个问题讲完,下一篇处理更根本的:缓存和数据库怎么写才不会脏。


本篇属于系列《S12 · Redis 与缓存》。旧文来源:A-879/2671/1840。

S12-J01 · 缓存穿透、击穿、雪崩
https://www.clxhxhhr.top/posts/5350/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。