只会说"加 Redis 缓存"不值钱,最在意的是:
- 你怎么保证一致性?
- 你怎么兜底?
- 出问题怎么排查?
你必须记住的 3 句话:
- Cache Aside(旁路缓存)是最常用主线:读先查缓存,未命中查 DB 并回填;写先写 DB,再删缓存。
- 缓存一致性没有银弹:你要明确允许的短暂不一致窗口,并用"删缓存 + 过期时间 + 幂等/校验"兜底。
- 线上缓存事故 80% 来自:穿透/击穿/雪崩 + 热点 key + 不合理 TTL,不是 Redis 本身性能不够。
1. Cache Aside 主线:为什么是"删缓存"而不是"更新缓存"
标准读写:
- 读:cache hit -> 返回;miss -> 查 DB -> 回填 cache
- 写:先写 DB -> 再删 cache
为什么写时不"更新缓存"?
- 更新缓存需要你构造完整的新值,容易遗漏字段
- 并发下更新顺序更难保证
- 直接删让后续读走 DB 回填,逻辑更简单
2. 双删 / 延迟双删:是在解决什么竞态
经典竞态:
- 线程 A:写 DB(新值),删 cache
- 线程 B:在 A 删 cache 之后、A 提交之前读 DB(读到旧值)并回填 cache
- 结果:缓存被旧值污染
延迟双删思路:
- 写 DB
- 删缓存
- 延迟一小段时间(让并发读写窗口过去)
- 再删一次
注意边界:
- 它降低概率,但不是强保证
- 延迟时间要结合事务提交与业务 RT
3. 过期时间(TTL):一致性的"最后兜底"
没有 TTL 的缓存一致性方案很危险:
- 一旦污染,就可能永久错误
工程建议:
- 重要 key 必须有 TTL
- TTL 分散(加随机抖动)避免同时过期
4. 三大缓存事故:穿透 / 击穿 / 雪崩
4.1 穿透:请求的 key 在 DB 也不存在
现象:
- 缓存永远 miss,DB 被打穿
常见手段:
- 缓存空值(短 TTL)
- 布隆过滤器
4.2 击穿:热点 key 过期
现象:
- 某个热点 key 过期瞬间,大量并发打到 DB
常见手段:
- 互斥重建(单飞)
- 热点 key 提前刷新
互斥重建(单飞)的最小落地思路(不追求完美,但要能讲清楚):
- miss 后先尝试抢一个"重建锁"(可以用本地锁/Redis 锁)
- 抢到的人去查 DB 并回填缓存
- 没抢到的人短暂等待后再读缓存(避免所有人一起打 DB)
工程提醒:
- 单飞锁的 TTL 要覆盖重建耗时,否则会出现"重建进行中锁过期"导致并发重建
4.3 雪崩:大量 key 同时过期
现象:
- 同一时间大量 miss,DB/RPC 雪崩
常见手段:
- TTL 加随机抖动
- 分批预热
- 限流/降级
5. 更强的一致性方案:CDC/消息驱动的缓存失效
当业务对一致性要求更高、并发更复杂时:
- 仅靠应用层删缓存会越来越难
思路:
- 以 DB 变更为"事实源",通过 binlog/CDC(Change Data Capture)驱动缓存失效或重建
优点:
- 一致性链路更集中
- 便于审计与回放
成本:
- 架构复杂度上升
- 需要处理消息堆积、重复、乱序(仍需幂等)
一个清晰的选型边界(面试非常加分):
- 允许短暂不一致:Cache Aside + TTL + 单飞 已足够
- 写一致性更敏感:延迟双删/消息驱动失效,明确不一致窗口
- 接近强一致诉求:以 DB 为事实源,走 Outbox/CDC/订阅 binlog,把一致性链路集中化
6. 常见坑:一致性写得"看似正确但会翻车"
-
坑 1:先删缓存再写 DB
- 并发读会回填旧值。
-
坑 2:没有 TTL
- 污染后长期错误。
-
坑 3:把缓存当数据库
- Redis 的持久化/复制不等于强一致。
-
坑 4:重建缓存没有互斥
- 热点 key 过期导致 DB 被打爆。
7. 线上排查:缓存相关慢与错怎么定位
先分两类:
- 慢:miss 高、DB/RPC 压力大
- 错:缓存值与 DB 值不一致
排查路径:
- 看命中率与热点 key(是否击穿/雪崩)
- 看 key 的 TTL 分布(是否同时过期)
- 看重建是否单飞(是否互斥)
- 对"错":
- 检查写路径是否"写 DB 后删 cache"
- 是否存在延迟双删/消息驱动
- 是否有回填旧值的窗口
8. 自测清单(你要能顺口讲出来)
-
Q:Cache Aside 写为什么是"写 DB 后删缓存"?
- A:避免并发读回填旧值,删除比更新更简单可靠。
-
Q:延迟双删解决什么?
- A:降低并发窗口里"读到旧值并回填缓存"的概率。
-
Q:穿透/击穿/雪崩怎么区分?
- A:穿透是 key 不存在;击穿是热点 key 过期;雪崩是大量 key 同时过期。
9. 30 秒背诵稿
缓存一致性最常用主线是 Cache Aside:读先查缓存,miss 查 DB 回填;写先写 DB 再删缓存,并用 TTL 作为最终兜底。并发下可能出现删缓存后读回填旧值的竞态,可用延迟双删降低概率,更高要求可以用 CDC/消息驱动统一失效。线上缓存事故主要是穿透、击穿、雪崩与热点 key,排查先看命中率、TTL 分布和重建是否单飞,再沿写路径确认是否遵循"写 DB 后删 cache"。