← 返回题目列表

什么是 Outbox Pattern?它如何解决本地事务和消息发送一致性?

高频 中等 第 5 / 25 题 更新于 2026/07/28
Outbox可靠消息本地消息表

简化版

Outbox Pattern 是在本地事务中同时写业务数据和消息事件表,事务提交后再异步把事件发送到消息队列。它解决的是“数据库更新成功但消息发送失败”或“消息发送成功但事务回滚”导致的数据不一致问题。

详细版

普通做法中,业务更新和发消息是两个动作:

  1. 更新订单状态。
  2. 发送订单已支付消息。

如果更新成功后应用宕机,消息就丢了;如果先发消息再提交事务,事务回滚后下游却收到假消息。

Outbox 的做法是:

  1. 在同一个本地事务内更新业务表。
  2. 在同一个本地事务内插入 outbox 事件表。
  3. 后台投递器扫描 outbox 表,发送 MQ。
  4. 发送成功后标记事件已发送。
  5. 发送失败继续重试。

它不能保证下游只消费一次,所以消费者仍要做幂等。

完整版教学

一、为什么发消息和写数据库会不一致

分布式系统里,写数据库和发 MQ 通常不是同一个事务资源。你很难用一个普通本地事务同时包住它们。

如果先写数据库再发消息:

更新订单成功
应用宕机
消息没发出去

下游库存、积分、通知服务就不知道订单变化。

如果先发消息再写数据库:

消息发出去了
数据库事务失败回滚

下游以为订单已支付,但订单库里并没有成功状态。这也很危险。

二、Outbox 的核心思想

Outbox 把“要发的消息”先当成本地数据保存下来。

在订单服务本地事务中:

update orders set status = 'PAID' where id = ?;
insert into outbox_event(event_id, event_type, payload, status)
values (?, 'OrderPaid', ?, 'NEW');

这两个操作由同一个数据库事务保证,要么都成功,要么都失败。

事务提交后,消息是否立刻发出去已经不是最关键的,因为 outbox 表里有可靠记录,可以稍后重试。

三、Outbox 投递器怎么工作

投递器可以是后台线程、定时任务或独立服务。它不断扫描 NEW 状态事件,发送到 MQ,成功后把状态改为 SENT

伪流程:

select NEW events limit 100
for each event:
    send MQ
    if success: mark SENT
    else: increase retry_count

为避免多个投递器重复抢同一事件,可以使用状态抢占、乐观锁、数据库锁或分片扫描。

四、Outbox 的重复消息问题

Outbox 常见语义是至少一次投递。比如 MQ 已经收到消息,但投递器更新事件状态时失败,那么下次扫描会再次发送同一事件。

所以消费者必须幂等。可以用 event_id 或业务 ID 建消费记录:

consumer_name + event_id 唯一

插入成功才处理,插入失败说明已处理过。

五、Outbox 和事务消息的关系

有些 MQ 提供事务消息能力,例如先发送半消息,再执行本地事务,最后提交或回滚消息。Outbox 则更通用,不强依赖 MQ 的事务能力。

Outbox 的优点是可控、可追踪、便于补偿;缺点是要维护事件表、投递任务、重试和归档。

六、常见误区与追问

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

回答层次要讲清的内容容易漏掉的边界
核心结论Outbox 把业务数据和待发送事件写入同一个本地事务,再由后台可靠投递事件,解决数据库和 MQ 双写窗口不要停在名词解释
流程机制业务事务写订单 -> 同事务写 Outbox 事件 -> 后台扫描未发送事件 -> 投递到 MQ -> 标记已发送 -> 消费者幂等处理要说清触发点、状态变化、确认点和失败兜底
工程取舍订单表写入和 outbox_event 写入同一事务成功后,即使进程崩溃,后台任务仍可扫描事件表补发消息一致性方案本质是在强一致成本、系统可用性、延迟和业务可接受的不一致窗口之间取舍
Outbox 模式 面试拆解:
1. 业务事务写订单
2. 同事务写 Outbox 事件
3. 后台扫描未发送事件
4. 投递到 MQ
5. 标记已发送
6. 消费者幂等处理

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

  • 误区:Outbox 能让 MQ 和数据库强一致。 它保证本地业务状态和待发送事件原子,MQ 投递仍是最终一致。
  • 误区:事件表可以无限增长。 需要归档、清理、分区和投递状态管理。
  • 误区:Outbox 不需要消费者幂等。 投递重试可能产生重复消息,消费者仍要幂等。
  • 追问:Outbox 解决什么问题? 解决业务写库成功但发消息失败的双写窗口。
  • 追问:后台投递失败怎么办? 按状态重试、指数退避、告警和人工处理。
  • 追问:Outbox 和 CDC 怎么结合? 可以让 CDC 订阅 Outbox 表变更并投递,减少业务进程发消息责任。

七、加强记忆

Outbox 的记忆点是“业务数据和消息记录同事务,真正发消息异步重试”。它不追求一次性把数据库和 MQ 变成强事务,而是先保证事件不丢,再靠投递重试和消费幂等实现最终一致。