1019 字
约 3 分钟
8
S12-J02 · 缓存与数据库一致性
适用读者:被"先删缓存还是先更库"绕晕的人。
前置:S12-J01、S11-A03。
覆盖旧文:870(102) 427(575) 2670(163) 2669(84) = 924 点。
覆盖扩展:E-S12-006(六种方案对比)。
上一篇:S12-J01 · 缓存穿透、击穿、雪崩
下一篇:S12-J03 · 分布式锁。
1. 一句话结论
缓存与 DB 不可能"强一致"(除非牺牲性能),只能做"最终一致"。六种方案的差别在"不一致窗口多长、实现多重"。
| 方案 | 窗口 | 复杂度 | 适用 |
|---|---|---|---|
| 先更库、再删缓存 | 毫秒级 | 低 | 默认推荐 |
| 先删缓存、再更库 | 较大 | 低 | ❌ 不推荐 |
| 延迟双删 | 更小 | 中 | 高并发写 |
| 订阅 binlog 删缓存 | 最小(秒级) | 中 | 追求稳定一致 |
| 定时对账 | 分钟+ | 低 | 兜底 |
| 强一致(分布式锁/2PC) | 0 | 极高 | 极少数场景 |
2. 为什么"先更库、再删缓存"是默认解
① 更新 DB
② 删除缓存
竞态分析:
| 时序 | 结果 |
|---|---|
| 读线程缓存 miss → 读 DB(旧值)→ 写缓存 | 若②在写缓存之后,缓存被删 → 下次读到新值 ✅ |
| 若②在写缓存之前 | 缓存留下旧值 ❌(窗口极小) |
"为什么不是更新缓存而是删除":更新缓存在并发下更容易产生"旧值覆盖新值"(两个写线程顺序错乱);删除是幂等的。
3. 六种方案对比(E-S12-006)
| # | 方案 | 关键点 |
|---|---|---|
| 1 | 先更库后删缓存(Cache Aside) | 业界默认 |
| 2 | 先删缓存后更库 | 窗口大,不推荐 |
| 3 | 延迟双删 | 更库前删、更库后延迟再删 |
| 4 | binlog 订阅 | 由数据变更驱动失效(最稳) |
| 5 | 定时对账 | 兜底修复 |
| 6 | 强一致(锁/2PC) | 性能代价极大 |
判据:"业务能容忍多久的旧值?" < 100ms → 方案 4;秒级 → 方案 1/3;无所谓 → 方案 1 + 对账。
4. 三个真实难点
| 难点 | 说明 |
|---|---|
| 删除失败 | 缓存删失败 → 旧值长期存在(要重试/补偿) |
| 主从延迟 | 更库后立即读从库可能读到旧值(S11-A02) |
| 多级缓存 | 本地缓存无法被"删除"通知到(要广播) |
"删除失败怎么补":重试队列 + 对账——这是工程上必须有的兜底。
5. 与 MongoDB/Cassandra 双写的共性(A-2669)
任何"两个存储双写"都有同一个问题:没有跨存储事务。所以:
① 优先选"单一事实来源 + 派生物由变更驱动"(binlog/CDC)
② 双写必须有对账
6. 五个误区
| 误区 | 真相 |
|---|---|
| "更新缓存比删除缓存好" | 并发下更易脏 |
| "延迟双删能根治" | 只是缩小窗口 |
| "删缓存失败无所谓" | 旧值会长期存在 |
| "多级缓存能靠删 Redis 失效" | 本地缓存要单独通知 |
| "强一致最好" | 性能代价常不可接受 |
7. 自检
- 六种方案的窗口与复杂度?
- 为什么默认是"先更库后删缓存"?
- "延迟双删"具体删两次的时机?
- 三个真实难点是什么?
- 双写为什么必须有对账?
8. 下一篇
S12-J03 · 分布式锁:SETNX → Redisson → RedLock
一致性讲完,下一篇讲"多实例互斥":锁怎么抢、怎么防误删(实测误删事故)、红锁为什么有争议。
本篇属于系列《S12 · Redis 与缓存》。旧文来源:A-870/427/2670/2669;扩展点 E-S12-006。
S12-J02 · 缓存与数据库一致性
https://www.clxhxhhr.top/posts/5351/ 评论
0 条
还没有评论,先写一条吧。