← 返回题目列表

RocketMQ 事务消息的原理是什么?半消息和事务回查怎么工作?

高频 中等 第 6 / 27 题 更新于 2026/07/28
事务消息RocketMQ半消息

简化版

RocketMQ 事务消息用来保证「本地事务执行」和「消息发送」两件事的原子性(要么都成、要么都不成)。核心是半消息(Half Message)+ 事务回查:先发一条对消费者不可见的「半消息」,再执行本地事务,本地事务成功就 commit(半消息转正、消费者可见),失败就 rollback(丢弃半消息);如果发起方在这中间宕机导致状态未知,MQ 会回查发起方的本地事务状态来决定最终 commit 还是 rollback。它相当于把「本地消息表」的机制内置进了 MQ。

详细版

完整流程(六步)

  1. 生产者向 MQ 发送半消息(Half Message)——消息已到 MQ,但标记为「暂不可投递」,消费者看不到。
  2. MQ 收到半消息,持久化并返回 ACK。
  3. 生产者收到 ACK 后,执行本地事务(如扣款、改订单)。
  4. 生产者根据本地事务结果向 MQ 提交二次确认
    • 成功 → Commit:MQ 把半消息转为正式消息,消费者可以消费。
    • 失败 → Rollback:MQ 丢弃这条半消息。
  5. 若 MQ 迟迟收不到二次确认(生产者在第 3、4 步之间宕机/超时),MQ 会主动回查(Check Back)生产者:这笔本地事务到底成没成?
  6. 生产者实现的回查接口查自己的业务状态,返回 Commit 或 Rollback,MQ 据此处理。

关键点

  • 半消息保证「消息先安全到 MQ,但暂不生效」,给本地事务留出执行窗口。
  • 事务回查兜住「生产者宕机导致二次确认丢失」的边界情况。
  • 事务消息只保证「本地事务 + 消息发送」原子,下游消费仍需幂等,且它保证的是最终一致(下游异步消费)。

完整版教学

一、要解决的问题:本地事务与发消息的原子性

和本地消息表一样,事务消息解决的是同一个经典难题:「执行本地事务」和「发一条 MQ 消息」如何保证要么都做、要么都不做。直接先发消息可能本地事务失败(消息多发),先做本地事务可能发消息失败(消息丢)。RocketMQ 用半消息机制在 MQ 层面解决它,好处是不用自己建本地消息表。

二、半消息:先占坑但不放行

半消息是精髓。生产者第一步就把消息发到 MQ,但 MQ 把它放进一个特殊的内部主题(RMQ_SYS_TRANS_HALF_TOPIC),对消费者不可见。这样做的意义是:消息已经安全落在 MQ 了(不会因后续生产者宕机而丢),但它还没「生效」,要等本地事务结果来决定放不放行。这等价于本地消息表里「待发送」状态的消息——已记录、未投递。

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

生产者执行完本地事务后,给 MQ 发第二次确认:

  • 本地事务成功 → 发 Commit → MQ 把半消息从内部主题「转正」到真实主题,消费者开始能消费。
  • 本地事务失败 → 发 Rollback → MQ 直接删掉这条半消息,消费者永远看不到。

到这一步,如果一切顺利,原子性就达成了:本地事务成 → 消息生效;本地事务败 → 消息作废。

四、事务回查:兜住「二次确认丢失」的边界

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

RocketMQ 的解法是主动回查:MQ 发现某条半消息长时间(默认一定时间后)没收到二次确认,就反向调用生产者实现的回查接口checkLocalTransaction),问它:「你那笔本地事务成功了吗?」生产者去查自己的业务数据(比如订单状态),返回 Commit 或 Rollback,MQ 据此处理。回查可以多次(默认最多 15 次),彻底兜住宕机边界。

记忆点:半消息保证「消息先到但不生效」,二次确认在正常路径下决定生效/作废,事务回查兜住「生产者宕机导致确认丢失」这个异常路径——三者配合才完整。

五、事务消息 vs 本地消息表

维度事务消息(RocketMQ)本地消息表
消息暂存MQ 内部半消息主题自己数据库的消息表
建表不用要建消息表
兜底机制MQ 事务回查后台任务扫表重投
依赖需 MQ 支持事务消息任意 MQ 均可
侵入实现回查接口维护消息表+投递任务
本质相同:保证本地事务与消息发送原子相同

选择:用 RocketMQ 且想少维护表 → 事务消息;MQ 不支持事务消息或想要更强掌控 → 本地消息表。

六、常见误区与追问

这道题不能只背概念,要把「事务消息」放回真实分布式系统里解释:谁发起、谁协调、状态如何变化、失败后怎么兜底,以及它和性能、可用性、一致性的取舍。

回答层次要讲清的内容容易漏掉的边界
核心结论事务消息通过半消息和事务回查,把本地事务结果与消息投递绑定,实现可靠最终一致不要停在名词解释
流程机制发送半消息 -> 执行本地事务 -> 提交或回滚半消息 -> MQ 异常时回查本地事务状态 -> 消费者幂等消费说明触发方、参与方、状态变化和兜底
工程取舍RocketMQ 先发送半消息,订单本地事务成功后提交消息,消费者才可见分布式事务没有免费强一致,性能、可用性和业务侵入一定要取舍
事务消息 面试拆解:
1. 发送半消息
2. 执行本地事务
3. 提交或回滚半消息
4. MQ 异常时回查本地事务状态
5. 消费者幂等消费

记忆钩子:先说本地事务失效的原因,再拆协调、补偿、消息、幂等、对账和回滚边界;回答时要紧扣「事务消息」这道题,不要把相邻概念混成一段泛泛的分布式套话。

  • 误区:事务消息保证全局强一致。 它保证消息与本地事务最终一致,下游消费仍是异步最终一致。
  • 误区:提交消息后消费一定成功。 消费者可能失败,需要重试、死信和幂等。
  • 误区:事务回查可以随便查。 回查必须基于可靠的本地事务状态表,不能靠内存状态。
  • 追问:半消息是什么? 对消费者暂不可见的预备消息,等待本地事务结果决定提交或回滚。
  • 追问:事务消息适合什么? 下单后异步通知库存、积分、优惠券等需要可靠触发的场景。
  • 追问:和本地消息表怎么选? 事务消息依赖 MQ 能力,本地消息表更通用但需要自建扫描投递。

七、加强记忆

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