缓存(Cache)是一种计算机存储技术,用于提高数据访问速度。它通过将频繁访问的数据存储在快速访问的存储介质中,以减少对主存储器(如硬盘)的访问次数,从而提高整个系统的性能。### 缓存的基本概念1. **存储介质**:缓存通常使用比主存储器(如RAM)更
摘要:这是后端面试必考、工程必踩的核心问题:更新数据库时,怎么保证缓存里的数据是对的? 我给你讲得原理通透、方案落地、能直接写进架构文档。 一、先搞懂:为什么会不一致? 本质只有一句话: 缓存和数据库是两个独立存储,无法原子更新。 只要是「先操作
这是后端面试必考、工程必踩的核心问题:更新数据库时,怎么保证缓存里的数据是对的?
我给你讲得原理通透、方案落地、能直接写进架构文档。
一、先搞懂:为什么会不一致?
本质只有一句话:
缓存和数据库是两个独立存储,无法原子更新。
只要是「先操作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只能做到最终一致
一句话:
更新数据库,删除缓存,最终一致。
