← 返回题目列表

什么是双写一致性问题?如何解决双写失败?

高频 中等 第 10 / 25 题 更新于 2026/07/28
双写一致性数据同步分布式事务

简化版

双写一致性问题是指一次业务操作要写两个资源,例如数据库和 MQ、数据库和缓存、两个服务数据库,其中一个成功另一个失败会造成不一致。解决方式包括本地事务加 Outbox、事务消息、可靠重试、补偿、对账,以及尽量避免同步双写。

详细版

双写常见场景:

  1. 写数据库后发 MQ。
  2. 更新数据库后删除缓存。
  3. 订单服务写订单,同时库存服务扣库存。
  4. 写 MySQL 后同步 Elasticsearch。

解决思路:

场景常见方案
DB + MQOutbox、本地消息表、事务消息
DB + Cache更新 DB 后删除缓存,失败重试
跨服务写Saga、TCC、可靠消息、补偿
DB + ESCDC、MQ 异步同步、对账重建

面试重点:双写不能只靠 try-catch,必须设计失败后的恢复路径和幂等机制。

完整版教学

一、双写问题为什么难

双写难在两个资源通常不属于同一个事务管理器。

例如:

更新订单数据库成功
发送订单消息失败

或者:

发送消息成功
更新数据库失败

只要中间有网络、进程、下游系统,就可能出现半成功状态。分布式系统不能假设两个动作永远一起成功。

二、DB 和 MQ 双写怎么处理

DB 和 MQ 双写最常用的是 Outbox 或事务消息。

Outbox 把业务数据和消息事件放进同一个本地事务,后续异步投递。事务消息则利用 MQ 的半消息和事务回查能力,让消息状态和本地事务结果关联。

两者目标都是避免:

业务成功但消息丢
消息发出但业务失败

三、DB 和缓存双写怎么处理

数据库和缓存双写不建议“更新数据库后直接更新缓存”,因为并发下旧值可能覆盖新值。

常用方案是更新数据库成功后删除缓存。删除失败要重试,缓存本身设置 TTL 兜底。对一致性要求更高的场景,可以使用延迟双删或 binlog 监听补删。

这类方案本质是最终一致:允许短时间旧缓存,但要保证旧缓存不会永久存在。

四、跨服务双写怎么处理

跨服务写更复杂。比如下单要创建订单和扣库存。

如果强一致要求很高,可以用 TCC:库存 Try 冻结,订单 Confirm 创建成功,失败 Cancel 释放库存。

如果可以最终一致,可以先创建订单,再发送库存扣减事件;库存失败后订单进入异常或取消补偿。

选择方案时要看业务能否接受中间状态。不是所有跨服务写都要 TCC,TCC 成本很高。

五、双写失败后的恢复路径

任何双写方案都要回答:

第一个写成功,第二个写失败怎么办?
第二个其实成功了,但响应超时怎么办?
重复重试会不会产生副作用?
长期不一致怎么发现?

答案通常是重试、幂等、补偿和对账组合,而不是某一个技术点。

六、如何从架构上减少双写

双写问题最好不是事后修,而是在建模时减少。比如一个服务拥有某份数据的写入权,其他系统通过事件订阅构建只读视图,而不是多个服务同时写同一份事实。

如果只是为了查询方便而双写,可以考虑读模型、宽表、缓存或搜索索引,这些都应被视为可重建的派生数据。派生数据不应该反过来成为权威事实。

如果两个写入都非常关键,要先判断是否真的属于同一个强一致边界。能放进同一个本地事务的,不要过早拆成跨服务双写;必须跨服务的,再考虑 TCC、Saga、Outbox 或事务消息。

七、常见误区与追问

这道题要紧扣「双写一致性问题」本身回答,不能把它混成泛泛的数据一致性套话。面试官通常会追问“写入怎么确认、失败怎么补、旧数据怎么防、成本在哪里”,所以回答要覆盖一致性目标、写入顺序、消息投递、幂等、补偿、对账和读写体验。

回答层次要讲清的内容容易漏掉的边界
核心结论双写问题是一次业务同时更新两个资源时无法保证两边原子成功,常见于数据库加缓存、数据库加 MQ不要停在名词解释
流程机制发起业务写入 -> 更新第一个资源 -> 更新第二个资源 -> 任一步失败产生不一致 -> 重试或补偿修复 -> 对账确认收敛要说清触发点、状态变化、确认点和失败兜底
工程取舍写库成功后发 MQ 失败,订单已支付但下游库存不知道;发 MQ 成功后写库回滚,下游又收到假事件一致性方案本质是在强一致成本、系统可用性、延迟和业务可接受的不一致窗口之间取舍
双写一致性问题 面试拆解:
1. 发起业务写入
2. 更新第一个资源
3. 更新第二个资源
4. 任一步失败产生不一致
5. 重试或补偿修复
6. 对账确认收敛

记忆钩子:先给结论,再拆流程,再讲数字例子和失败边界;回答「双写一致性问题」时要围绕题目问法收束,不要把相邻概念堆成一段没有重点的名词清单。

  • 误区:先写 A 再写 B 就能保证一致。 两步之间任何崩溃、超时或网络失败都会留下不一致窗口。
  • 误区:把顺序反过来就彻底解决。 先发消息还是先写库都有另一类失败窗口。
  • 误区:重试一定能解决双写。 重试要有幂等和可观测,否则可能重复副作用或无限重试。
  • 追问:数据库和 MQ 双写怎么治理? 本地消息表、Outbox、事务消息、CDC 或可靠消息最终一致。
  • 追问:缓存双写常用什么? 先更新数据库再删除缓存,配合重试、TTL、延迟双删和 binlog 兜底。
  • 追问:强一致能不能做? 可以用分布式事务,但成本、可用性和性能代价更高。

八、加强记忆

双写一致性问题的本质是“两个资源不能被一个普通本地事务同时保护”。解决它要么把其中一个写入变成本地可靠事件,要么使用事务消息/TCC/Saga,要么接受最终一致并配套重试、幂等、补偿和对账。