一文搞懂:缓存 & 数据库 数据一致性原理 + 全套方案
这是后端面试必考、工程必踩的核心问题:更新数据库时,怎么保证缓存里的数据是对的?
我给你讲得原理通透、方案落地、能直接写进架构文档。
一、先搞懂:为什么会不一致?
本质只有一句话:
缓存和数据库是两个独立存储,无法原子更新。
只要是「先操作A,再操作B」两步动作,就一定可能出现:
- 第一步成功
- 第二步失败/超时/延迟
→ 数据不一致
常见脏数据场景:
- 线程1更新DB → 还没删缓存
- 线程2读缓存 → 读到旧数据
- 线程1才删缓存
→ 业务读到脏数据
二、最关键结论(先记死)
所有正确方案,都遵守一条铁律:
写数据时,一定是「更新数据库 + 淘汰缓存」,绝对不能「更新数据库 + 更新缓存」。
原因:
- 更新缓存 = 写放大,高并发下浪费CPU
- 并发更新会产生覆盖,导致永久脏数据
三、四种经典策略(原理+优缺点+场景)
1)Cache Aside Pattern(最常用、业务主流)
流程
读:命中直接返回,不命中读DB → 回填缓存
写:更新DB → 删除缓存
为什么是删除,不是更新?
因为:
- 你不知道这次更新后,有没有人会读
- 不读就不需要写缓存
- 写缓存=无效开销
可能问题
更新DB后,删除缓存失败 → 脏数据
适用
90% 业务系统、订单、用户、商品等。
2)Write Through(写穿透)
流程
DB 和缓存 同步更新,一起成功/失败
需要强事务支持,Redis本身不支持。
问题
性能差,几乎不用。
3)Write Behind(异步写)
流程
写缓存 → 立即返回 → 异步刷DB
问题
丢数据风险极大,不适合金融/订单核心链路。
4)Read Through(读穿透)
由缓存组件自己负责读DB,业务不关心。
常见于框架,不常用。
四、真正核心:先更DB?还是先删缓存?
这是面试必考题。
错误方案:先删缓存,再更新DB
一定会产生脏数据:
- 线程1删缓存
- 线程2读DB → 旧数据 → 写入缓存
- 线程1更新DB
→ 缓存旧,DB新,永久不一致
正确方案:先更新DB,再删缓存
这是业界标准正确姿势。
五、先更新DB、再删缓存,还有问题吗?
有!极端并发下会短暂不一致。
极端场景(概率极低)
- 线程A读:缓存不命中 → 读DB旧值
- 线程B写:更新DB新值 → 删除缓存
- 线程A把旧值写回缓存
→ 缓存脏了
结论
- 概率极低
- 是短暂不一致,不是永久脏数据
- 对绝大多数业务可接受
六、工业级一致性方案(能落地的)
方案1:延迟双删(简单实用)
流程:
- 更新DB
- 删除缓存
- 延迟几百ms,再删一次
作用:
把刚才那种极端并发写入的旧缓存删掉。
优点:简单、稳定、99%场景够用。
方案2:删除缓存重试机制(高可靠)
删除缓存失败怎么办?
- MQ重试
- 定时任务重试
- 分布式任务重试
保证:最终一定能删掉。
方案3:Canal/订阅binlog异步淘汰(最稳)
流程:
- 业务只写DB
- Canal订阅binlog
- 解析到更新 → 异步删除缓存
优点:
- 业务无侵入
- 一致性最强
- 高并发下最稳
大厂主流方案。
七、最终一致性 vs 强一致性
Redis + DB 只能保证:
最终一致性
强一致性怎么做?
- 2PC(性能差)
- TCC(复杂)
- Paxos/Raft(太重)
缓存场景几乎不用
八、最清晰总结(面试直接背)
- 缓存不一致根源:DB与缓存无法原子更新
- 正确写策略:先更新DB,再删除缓存
- 绝对不能:先删缓存,再更DB
- 绝对不能:更新缓存,只能删除
- 高一致推荐:Canal binlog异步淘汰
- 简单可靠:延迟双删
- 缓存+DB只能做到最终一致
一句话:
更新数据库,删除缓存,最终一致。
百流积聚,江河是也;文若化风,可以砾石。

浙公网安备 33010602011771号