一文搞懂:缓存 & 数据库 数据一致性原理 + 全套方案

这是后端面试必考、工程必踩的核心问题:更新数据库时,怎么保证缓存里的数据是对的?

我给你讲得原理通透、方案落地、能直接写进架构文档


一、先搞懂:为什么会不一致?

本质只有一句话:
缓存和数据库是两个独立存储,无法原子更新。

只要是「先操作A,再操作B」两步动作,就一定可能出现:

  • 第一步成功
  • 第二步失败/超时/延迟
    数据不一致

常见脏数据场景:

  1. 线程1更新DB → 还没删缓存
  2. 线程2读缓存 → 读到旧数据
  3. 线程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. 线程1删缓存
  2. 线程2读DB → 旧数据 → 写入缓存
  3. 线程1更新DB
    缓存旧,DB新,永久不一致

正确方案:先更新DB,再删缓存

这是业界标准正确姿势。


五、先更新DB、再删缓存,还有问题吗?

有!极端并发下会短暂不一致

极端场景(概率极低)

  1. 线程A读:缓存不命中 → 读DB旧值
  2. 线程B写:更新DB新值 → 删除缓存
  3. 线程A把旧值写回缓存
    缓存脏了

结论

  • 概率极低
  • 短暂不一致,不是永久脏数据
  • 对绝大多数业务可接受

六、工业级一致性方案(能落地的)

方案1:延迟双删(简单实用)

流程:

  1. 更新DB
  2. 删除缓存
  3. 延迟几百ms,再删一次

作用:
把刚才那种极端并发写入的旧缓存删掉。

优点:简单、稳定、99%场景够用。


方案2:删除缓存重试机制(高可靠)

删除缓存失败怎么办?

  • MQ重试
  • 定时任务重试
  • 分布式任务重试

保证:最终一定能删掉


方案3:Canal/订阅binlog异步淘汰(最稳)

流程:

  1. 业务只写DB
  2. Canal订阅binlog
  3. 解析到更新 → 异步删除缓存

优点:

  • 业务无侵入
  • 一致性最强
  • 高并发下最稳

大厂主流方案。


七、最终一致性 vs 强一致性

Redis + DB 只能保证:

最终一致性

强一致性怎么做?

  • 2PC(性能差)
  • TCC(复杂)
  • Paxos/Raft(太重)
    缓存场景几乎不用

八、最清晰总结(面试直接背)

  1. 缓存不一致根源:DB与缓存无法原子更新
  2. 正确写策略:先更新DB,再删除缓存
  3. 绝对不能:先删缓存,再更DB
  4. 绝对不能:更新缓存,只能删除
  5. 高一致推荐:Canal binlog异步淘汰
  6. 简单可靠:延迟双删
  7. 缓存+DB只能做到最终一致

一句话:
更新数据库,删除缓存,最终一致。


posted @ 2026-03-08 16:25  七星6609  阅读(7)  评论(0)    收藏  举报