缓存一致性:Cache Aside、双删/延迟双删、穿透/击穿/雪崩与 CDC

只会说"加 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"。

相关推荐
Yyyyyy~2 分钟前
【java】数组的定义和使用
java
麻辣布丁3 分钟前
java集合篇面试模拟
java·开发语言·面试
niucloud-admin5 分钟前
JAVA V6 多商户商城 开发文档——插件开发规范
java·开发语言
嘟哩DuliDuli17 分钟前
创作 Agent 的任务状态该如何保存
android·java·javascript
wdfk_prog30 分钟前
lely-canopen-rtt:为什么推荐在 RT-Thread 上使用 Lely CANopen 构建主站
java·运维·缓存·docker·容器
Token掘金室35 分钟前
JSON模式结构化输出报错
java·服务器·json
秋饼41 分钟前
Spring AI 2.0 接入 DeepSeek V4.1 Flash 生产级实战
java·ai·技术分享·后端开发
IT枫斗者枫哥41 分钟前
Java 分批导出仍然 OOM?用 32 MiB 堆复现三种 CSV 写法
java
shehuiyuelaiyuehao1 小时前
算法43,外观数列,模拟算法+双指针
java·算法
lhldsg1 小时前
多商户团购系统源码:技术架构选型与二次开发实战指南
java·小程序·架构