← 返回题目列表

RocketMQ 的事务消息是怎么实现的?半消息机制是什么?

高频 中等 第 16 / 26 题 更新于 2026/07/28
事务消息半消息RocketMQ分布式事务

简化版

RocketMQ 事务消息解决「本地事务执行」和「发消息」的原子性问题(要么都成、要么都不成),核心是半消息 + 事务回查:① 生产者先发一条半消息(已到 Broker 但对消费者不可见);② 执行本地事务;③ 本地事务成功就发 Commit(半消息转正、消费者可见),失败就发 Rollback(丢弃半消息);④ 如果生产者宕机导致第二步确认丢失,Broker 会回查生产者的本地事务状态来决定提交还是回滚。它保证了「本地事务成功 ⟺ 消息发出」的最终一致。

详细版

要解决的问题:本地事务(如扣款、改订单)和「发一条 MQ 消息」这两件事,要么都做、要么都不做。直接先发消息可能本地事务失败(消息多发),先做本地事务可能发消息失败(消息丢),二者无法用一个事务包住。

六步流程

  1. 生产者发送半消息到 Broker——消息已存储,但标记为「暂不可投递」,消费者看不到。
  2. Broker 持久化半消息,返回成功。
  3. 生产者执行本地事务
  4. 生产者根据本地事务结果发送二次确认:
    • 成功 → Commit:Broker 把半消息转为正式消息,消费者可消费。
    • 失败 → Rollback:Broker 丢弃半消息。
  5. 若 Broker 长时间未收到二次确认(生产者在第 3-4 步之间宕机),Broker 主动回查生产者。
  6. 生产者实现的回查接口检查本地事务状态,返回 Commit / Rollback。

完整版教学

一、半消息:先占位、不放行

半消息(Half Message)是事务消息的核心。生产者第一步就把消息发到 Broker,但 Broker 把它放进一个特殊的内部主题,对消费者不可见。为什么这么设计?因为它要在「消息已经安全落到 Broker(不会因生产者后续宕机而丢)」和「消息还不能被消费(要等本地事务结果)」之间取得平衡。半消息就是这个「已存储但未生效」的中间态——占了位,但先不放行,等本地事务的结果来决定命运。

二、二次确认:本地事务结果决定放行还是丢弃

生产者存完半消息后,去执行本地事务(扣款、改库存等)。执行完,根据结果给 Broker 发第二次确认:

  • 本地事务成功 → 发 Commit → Broker 把半消息「转正」,投递给消费者。
  • 本地事务失败 → 发 Rollback → Broker 删除半消息,消费者永远看不到。

到这一步,正常情况下原子性就达成了:本地事务成 → 消息生效;本地事务败 → 消息作废。这样下游消费者收到消息,就意味着上游的本地事务一定成功了。

记忆点:半消息「先存到 Broker 但消费者不可见」,二次确认按本地事务结果决定「Commit 转正 / Rollback 丢弃」,事务回查兜住「生产者宕机导致确认丢失」的异常。三者配合实现本地事务与发消息的原子性。

三、事务回查:兜住宕机的边界

最棘手的情况:生产者执行完本地事务,但在发二次确认之前宕机了,或者确认消息在网络中丢了。此时 Broker 手里攥着一条半消息,却不知道该 Commit 还是 Rollback(本地事务到底成没成?)。

RocketMQ 的解法是主动回查:Broker 发现某条半消息长时间没收到二次确认,就反向调用生产者实现的回查接口checkLocalTransaction),问它「你那笔本地事务成功了吗?」生产者去查自己的业务数据(如订单状态),返回 Commit 或 Rollback,Broker 据此处理。回查会重复多次(默认最多 15 次)。这一步彻底兜住了「生产者宕机导致确认丢失」的异常路径,让机制完整可靠。

四、注意:只保证「本地事务 + 发消息」原子,不保证下游一定成功

事务消息容易被误解的一点:它保证的是生产端「本地事务和消息发送」的原子一致,不保证消费端一定处理成功。消息投递给消费者后,消费者能不能成功处理是另一回事——那要靠消费端的重试 + 幂等来保证(消费失败会重投,重投要幂等)。所以事务消息实现的是「上游本地事务成功 → 消息一定发出 → 下游最终会消费成功」的最终一致,而不是端到端的强一致。整条链路的可靠性 = 事务消息(生产端原子)+ 消费端幂等重试。

五、和本地消息表的关系

事务消息和「本地消息表」解决的是同一个问题,实现方式不同:

  • 本地消息表:把待发消息存到自己数据库的一张表里(和业务操作同事务),再由后台任务轮询投递到 MQ。要自己建表、写投递任务。
  • 事务消息:把「暂存 + 确认 + 回查」的机制内置在 MQ(RocketMQ),不用自己建表,但要实现回查接口,且依赖 MQ 支持事务消息。

二者思想一致(都保证本地事务与消息发送的原子性),事务消息可看作「本地消息表的 MQ 内置版」。

六、常见误区与追问

这道题不能只背概念,要把「MQ 事务消息」放回真实分布式系统里解释:参与方是谁、状态怎么流转、失败后怎么恢复,以及它在一致性、性能、可用性之间做了什么取舍。

回答层次要讲清的内容容易漏掉的边界
核心结论事务消息通过半消息、提交/回滚和事务回查,保证本地事务结果与消息可见性最终一致不要停在名词解释
流程机制发送半消息 -> 执行本地事务 -> 根据结果提交或回滚 -> Broker 超时回查事务状态 -> 提交后消费者处理 -> 消费端幂等兜底说明触发方、存储方、确认点和兜底
工程取舍订单本地事务成功后提交半消息,库存消费者才看到扣减事件;本地事务失败则回滚半消息MQ 解耦削峰但带来最终一致、重复消费和可观测性要求
MQ 事务消息 面试拆解:
1. 发送半消息
2. 执行本地事务
3. 根据结果提交或回滚
4. Broker 超时回查事务状态
5. 提交后消费者处理
6. 消费端幂等兜底

记忆钩子:先拆生产者、Broker、消费者、offset、重试和幂等,再说明丢失、重复、顺序和积压边界;回答时要紧扣「MQ 事务消息」这道题,不要把相邻概念混成一段泛泛的分布式套话。

  • 误区:事务消息保证消费者业务一定成功。 它只保证本地事务和消息投递一致,消费端仍可能失败并重试。
  • 误区:事务回查可以查内存。 必须查可靠的本地事务状态或业务表。
  • 误区:事务消息等于分布式强一致。 它是异步最终一致方案。
  • 追问:半消息为什么消费者不可见? 避免本地事务未确定时下游提前消费。
  • 追问:回查解决什么? 生产者提交或回滚结果丢失时,Broker 主动确认本地事务状态。
  • 追问:下游重复消费怎么办? 消费者按业务 ID 做幂等。

七、加强记忆

RocketMQ 事务消息用「半消息 + 事务回查」保证本地事务与发消息的原子性:① 发半消息(到 Broker 但消费者不可见);② 执行本地事务;③ 成功发 Commit(半消息转正可消费)、失败发 Rollback(丢弃);④ 若生产者宕机导致二次确认丢失,Broker 主动回查生产者的本地事务状态来决定。它只保证生产端「本地事务成功⟺消息发出」,下游能否成功要靠消费端幂等+重试,整体是最终一致。它是「本地消息表」的 MQ 内置版,省去自建消息表但要实现回查接口。