系统架构设计师 · 高频练习

数据库写成功但消息发送失败,Outbox 模式怎样处理双写不一致?

高级 单选题 第 905 题 中等 系统架构设计师Transactional Outbox双写一致性消息队列幂等
题目

订单服务需要同时写入本地数据库并向消息队列发布“订单已创建”事件。为减少数据库提交成功、消息发送失败造成的不一致,采用 Transactional Outbox 模式时,较合理的做法是()。

A 订单写库和消息发送完全并行,谁失败都不影响另一方
B 先发送消息,等待消费者完成后再尝试写订单,失败就不记录
C 把数据库事务超时时间调得足够长,保证消息系统永不失败
D 在同一本地事务中写订单和 Outbox 事件记录,提交后由独立投递器重试发送
题目类型:原创高频练习题 用途:用于帮助理解系统架构设计师相关考点和答案解析,不等同于官方真题。
正确答案
D
答案解析

Outbox 将业务数据和待发布事件写入同一个本地数据库事务,使二者要么一起提交、要么一起回滚。事务提交后,再由轮询器或日志捕获组件把 Outbox 事件可靠投递到消息系统。

选项分析

A

错误。完全并行没有原子边界,正是双写不一致的来源。

B

错误。先发消息仍会留下消息成功、订单写入失败的反向不一致窗口。

C

错误。延长事务不能让外部消息系统永不失败,还会增加锁持有和资源占用。

D

正确。业务数据与事件记录共享本地事务,提交后再进行可重试投递。

本题为什么容易错

Outbox 常被误解成“事务提交后发一次消息”。它真正保存的是可恢复的发送意图;投递至少一次更常见,所以不能省略幂等消费。

先看结论

简短答案

数据库写成功但消息发送失败,Outbox 模式怎样处理双写不一致,正确答案是 D(在同一本地事务中写订单和 Outbox 事件记录,提交后由独立投递器重试发送)。Outbox 将业务数据和待发布事件写入同一个本地数据库事务,使二者要么一起提交、要么一起回滚。事务提交后,再由轮询器或日志捕获组件把 Outbox 事件可靠投递到消息系统。

解析

易混淆概念对比表

概念本题判断区别要点记忆提示
订单写库和消息发送完全并行,谁失败都不影响另一方 本题干扰项 错误。完全并行没有原子边界,正是双写不一致的来源。 看到该词不要急着选,先判断是否真正解决题干问题
先发送消息,等待消费者完成后再尝试写订单,失败就不记录 本题干扰项 错误。先发消息仍会留下消息成功、订单写入失败的反向不一致窗口。 看到该词不要急着选,先判断是否真正解决题干问题
把数据库事务超时时间调得足够长,保证消息系统永不失败 本题干扰项 错误。延长事务不能让外部消息系统永不失败,还会增加锁持有和资源占用。 看到该词不要急着选,先判断是否真正解决题干问题
在同一本地事务中写订单和 Outbox 事件记录,提交后由独立投递器重试发送 本题正确答案 正确。业务数据与事件记录共享本地事务,提交后再进行可重试投递。 看到题干核心场景时优先联想到它
本题易混淆选项怎么区分
  • 订单写库和消息发送完全并行,谁失败都不影响另一方:错误。完全并行没有原子边界,正是双写不一致的来源。
  • 先发送消息,等待消费者完成后再尝试写订单,失败就不记录:错误。先发消息仍会留下消息成功、订单写入失败的反向不一致窗口。
  • 把数据库事务超时时间调得足够长,保证消息系统永不失败:错误。延长事务不能让外部消息系统永不失败,还会增加锁持有和资源占用。
复习

知识点详解

Transactional Outbox 适合业务数据库是可靠事实源、又需要异步发布领域事件的场景。投递器可以轮询 Outbox 表,也可以使用 CDC 读取数据库变更日志。它降低了分布式事务耦合,但会引入事件延迟、重复投递、表清理和顺序保证等工程问题,需要配套可观测性和幂等机制。

备考速记

先把“要发消息”与业务一起落库,再慢慢投;不怕暂时失败,但要防重复。

Transactional Outbox 在幂等场景中的作用

Transactional Outbox在本题中的核心价值,是解决“订单服务需要同时写入本地数据库并向消息队列发布“订单已创建”事件。为减少数据库提交成功、消息发送失败造成的不一致,采用 Transactional Outbox 模式时,较合理的做法是()”这个场景问题。复习时不要只背选项名称,还要理解它为什么适用于该场景,以及它能解决哪类安全、流程或管理问题。

拓展

同类题怎么考

  • 数据库与消息队列双写失败,选择一致性方案。
  • 判断 Outbox、Saga、TCC 和两阶段提交各自解决的边界。
Transactional Outbox 在系统架构设计师软考中的考法

软考选择题通常不会只考概念定义,还会把Transactional Outbox放到幂等场景中,要求判断它的作用、适用范围或与相近概念的区别。遇到这类题时,先抓住题干中的业务场景,再看哪个选项最能解决该场景下的核心问题。

解题思路

先把问题拆开:数据库和消息队列不是同一个本地事务资源,代码里顺序调用两次就一定存在中间失败窗口。Outbox 不强行把两个系统绑成一个大事务,而是先在数据库内部完成一次原子写入:订单一行,事件一行。只要提交成功,事件就不会凭空丢失;后台投递失败可以继续重试。至于重复消息,则交给事件 ID 和消费者幂等处理。

考点定位

Outbox 解决的是原子记录业务状态与待发事件,不保证天然只投递一次。投递器可能重试,消费者仍需幂等和去重。

易错提醒

  • 只设计 Outbox 表,没有给投递任务做重试、状态和监控。
  • 假设消息绝不会重复,消费者直接执行扣款或发货。
  • 过早删除 Outbox 记录,故障后缺少补发和审计依据。

备考提示

  • 架构题写 Outbox 时,把本地事务、可靠投递、幂等消费三段写全。
  • 再补充事件 ID、失败重试、死信或人工补偿,答案会更完整。

你可能还想了解

  • Outbox 模式能保证消息只发送一次吗?
  • Outbox 和本地消息表有什么区别?
  • Outbox 投递失败和重复消费怎么处理?

本文小结

Outbox把订单和待发事件放进同一个本地事务,消除数据库成功但发送意图丢失的窗口;后台投递器可持续重试。它通常是至少一次投递,因此消费者仍要按事件ID做幂等。