【终极解答】如何保证数据库和缓存双写一致性
我们今天来聊一个在后端面试中几乎无法回避,甚至被戏称为“价值百万”的面试题:如何保证数据库和缓存的双写一致性?

屏幕前的你可能已经接触过像延迟双删、消息队列这些方案,但你是否真的理解它们背后的并发陷阱和脏数据问题?如果你没有理解这些问题,而只是直接回答各种方案,那么面试官很容易发现你是背的。这样的答案还不如不答。
今天,我们就从根源出发,把这个知识点彻底搞懂,让你在面试中游刃有余。
那么,问题到底出在哪?我们来看屏幕上的这个最常见的场景。

你的应用更新了数据库,现在数据库里存的是最新的数据 v2。但与此同时,一个用户的读请求来了。
它先打到缓存,哎,缓存里还是旧数据 v1!结果,用户就读到了过时的数据。明明数据库已经更新了,用户却毫无感知。这就是我们今天要解决的核心痛点:数据不一致。
最简单粗暴的方法是什么?加锁。

就像屏幕上展示的,无论来多少个读写线程,都给我排好队,一个一个来。当一个线程在更新数据时,它会锁住资源,其他所有线程都必须等待。
这样做的好处显而易见:数据绝对不会出错,我们获得了强一致性。但缺点也同样致命:它是个性能杀手。在高并发场景下,系统吞吐量会急剧下降,这在现代互联网应用中几乎是不可接受的。
好,既然不能用锁,那我们是不是只要规定好操作顺序就行了?我们先来看方案A:先删除缓存,再更新数据库。

请看屏幕上的时序图:
1、请求 A 先把缓存删了。
2、就在它准备更新数据库的瞬间,请求 B 来了,它要读数据。
3、请求 B 发现缓存是空的,于是就去读数据库,注意,它读到的是旧数据 v1。
4、然后,请求 B 把这个旧数据 v1 写回了缓存。
5、最后,请求 A 才完成了数据库的更新,写入了新数据 v2。
最终结果是什么?数据库是新数据,缓存是旧数据,数据永久都不一致了!所以,这个方案是绝对不可取的。那我们反过来试试呢?
我们来看第二种方案:方案B:先更新数据库,再删除缓存。

同样看这个时序图:
1、请求 A 先把数据库更新为 v2。
2、在它删除缓存之前,请求 B 读了缓存,拿到了旧数据 v1。这时确实有短暂的不一致。
3、但紧接着,请求 A 成功删除了缓存。
重点来了:当下一个读请求进来时,它会发现缓存是空的,就会去数据库读取最新的数据 v2,并写回缓存。系统通过这个机制,自动完成了修复。
结论很明确:虽然可能发生短暂的不一致,但系统可以自我修复,这是相对更安全的模式。
刚刚的方案 B 已经很不错了,但它依然有一个极小概率的风险。为了解决这个风险,就有了“延迟双删”这个优化方案。

看屏幕上的流程,它其实就是在我们刚刚方案 B 的基础上,增加了一个“保险”步骤:
1、先删除缓存。
2、然后更新数据库。
3、最后,等待几百毫秒,再删除一次缓存。
这第二次删除,就是为了干掉在更新数据库期间,可能被其他读请求写入的脏数据。它能大大降低数据不一致的概率,但缺点是这个延迟时间不好评估,而且也牺牲了一部分写入性能。
如果想让系统更可靠、性能更高,我们就需要引入异步思想。这就是基于消息队列的方案。

看这个架构图,写请求的流程变了:
1、它先更新数据库。
2、然后,它不直接操作缓存,而是发一条消息到像 Kafka 或 RabbitMQ 这样的消息队列里,然后就直接返回了。主业务流程非常快。
3、我们有一个独立的消费者服务,它会从消息队列里获取消息,然后由它来执行删除缓存的操作。
这样做的好处是业务解耦,核心业务性能高,而且消息队列天生支持重试,非常可靠。当然,代价就是架构变重了,并且系统进入了“最终一致性”的状态。
还有没有更优雅的方案?有,那就是订阅数据库的 Binlog。

看屏幕上这个终极架构。我们的业务代码变得极其纯粹:它只负责更新数据库,完全不用关心缓存的存在。
1、当数据库发生变更时,它会自己记录一条 Binlog(二进制日志)。
2、我们用一个像 Canal 这样的中间件,伪装成一个数据库的从库,去订阅并解析这个 Binlog。
3、当 Canal 解析到数据变更后,由它来通知缓存进行删除。
这个方案实现了对业务代码的零侵入,是解耦最彻底、可靠性最高的方案。当然,它的复杂度也是所有方案中最高的。
好了,我们回顾一下。从加锁的强一致,到延迟双删的准实时,再到消息队列和订阅 Binlog的最终一致。

正如屏幕上这个表格总结的:
你如果追求强一致性,就要牺牲性能(加锁)。
如果你想要最高的性能和解耦,就要接受更高的复杂度(Binlog)。
记住,技术选型没有银弹,只有最适合当前业务场景的方案。这是一种权衡的艺术,如何找到各种方案最佳的平衡点,这种能力,恰恰就是区分一个普通码农和一个优秀程序员的地方。而这也是我一直强调的AI时代程序员最重要的能力。
最后,大家听完还有什么问题欢迎评论区交流。另外,我这边给大家整理了一份120万字的面试宝典,里面有更详细的图文版本面试资料,以及类似的几百个项目场景面试问题,最近要面试的同学留下888。