news 2026/6/3 1:14:20

事务消息是什么

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
事务消息是什么

你可以先把事务消息理解成一句话:

事务消息是用来保证“本地数据库操作”和“发送 MQ 消息”尽量保持一致的。

它解决的不是“消费者一定成功消费”的问题,而是解决:

我数据库里的业务操作成功了,MQ 消息也应该成功发出去。 我数据库里的业务操作失败了,MQ 消息就不应该被消费者看到。

1. 先看普通消息有什么问题

假设你有一个下单接口:

1. 创建订单,写入数据库 2. 发送 MQ 消息,通知库存系统扣库存

代码可能像这样:

publicvoidcreateOrder(){// 1. 创建订单,写数据库orderService.saveOrder();// 2. 发送 MQ 消息rocketMQTemplate.convertAndSend("order_topic:create","订单创建成功");}

看起来没问题,但它有两个风险。


情况一:订单创建成功了,但消息发送失败了

订单写入数据库成功 ✅ 发送 MQ 消息失败 ❌

结果就是:

数据库里有订单 但是库存系统不知道 所以库存没有扣

这就出现了数据不一致。


情况二:消息发送成功了,但订单创建失败了

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

publicvoidcreateOrder(){// 1. 先发消息rocketMQTemplate.convertAndSend("order_topic:create","订单创建成功");// 2. 再创建订单orderService.saveOrder();}

可能出现:

MQ 消息发送成功 ✅ 订单写入数据库失败 ❌

结果就是:

库存系统收到消息,开始扣库存 但是数据库里根本没有这个订单

这也不对。


2. 所以事务消息要解决什么?

事务消息就是要解决这个问题:

本地事务,比如创建订单 MQ 消息发送

这两个动作要配合起来。

它想达到的效果是:

订单创建成功 -> 消息才能被消费者消费 订单创建失败 -> 消息不能被消费者消费

3. RocketMQ 事务消息的核心思想

RocketMQ 不是一上来就把消息给消费者。

它会先发一条特殊消息,叫:

半消息 Half Message

你可以理解为:

消息先放到 RocketMQ 里,但是暂时不让消费者看到。

就像你寄快递时,先把快递放到快递站,但告诉快递站:

先别派送,等我确认。

4. 事务消息完整流程

假设订单系统创建订单,然后通知库存系统扣库存。

流程是这样:

1. 订单系统先发送半消息到 RocketMQ 2. RocketMQ 保存半消息,但消费者暂时看不到 3. 订单系统执行本地事务,也就是创建订单 4. 如果订单创建成功,告诉 RocketMQ:提交消息 5. RocketMQ 把消息变成可消费状态 6. 库存系统才能消费消息,开始扣库存

如果订单创建失败:

1. 订单系统先发送半消息到 RocketMQ 2. RocketMQ 保存半消息,但消费者暂时看不到 3. 订单系统执行本地事务,创建订单失败 4. 告诉 RocketMQ:回滚消息 5. RocketMQ 删除/丢弃这条消息 6. 库存系统永远看不到这条消息

5. 用生活例子理解

你把 RocketMQ 想成快递站。

普通消息是:

你把快递交给快递站 快递站马上派送

事务消息是:

你把快递交给快递站 但是贴了一个“暂不派送”的标签

然后你去确认一件事:

钱有没有付成功? 订单有没有创建成功? 数据库有没有写成功?

如果确认成功:

你告诉快递站:可以派送了

如果确认失败:

你告诉快递站:这个快递取消,不要派送

所以事务消息的关键不是“消费者消费成功”,而是:

消费者看到消息之前,先确认生产者自己的本地事务成功了。

6. 为什么叫“事务消息”?

因为它和数据库事务有关。

数据库事务是:

要么都成功 要么都失败

比如转账:

A 扣 100 B 加 100

不能只扣 A,不加 B。

而 RocketMQ 事务消息想解决的是:

数据库操作 MQ 消息投递

这两个动作的一致性问题。

比如:

订单入库成功 订单消息也应该发出去

不能出现:

订单入库成功,但是消息没发出去

也不能出现:

订单没入库,但是消息发出去了

7. 事务消息和普通消息的区别

普通消息:

消息一发出去,消费者就可能收到。

事务消息:

消息先进入 RocketMQ,但消费者暂时看不到。 等本地事务成功后,消息才会被消费者看到。

对比一下:

普通消息: 生产者 -> Broker -> 消费者 事务消息: 生产者 -> 半消息 -> 执行本地事务 -> 提交消息 -> 消费者

8. 最核心的三个状态

RocketMQ 事务消息有三个结果:

COMMIT:提交消息,消费者可以消费 ROLLBACK:回滚消息,消费者看不到 UNKNOWN:暂时不知道结果,RocketMQ 后面会回查

这三个很重要。


1)COMMIT

本地事务成功。

比如:

订单创建成功

于是告诉 RocketMQ:

这条消息可以投递给消费者了。

2)ROLLBACK

本地事务失败。

比如:

订单创建失败

于是告诉 RocketMQ:

这条消息不要了,消费者不应该看到。

3)UNKNOWN

生产者执行本地事务后,可能因为网络问题、服务宕机等原因,没有告诉 RocketMQ 到底成功还是失败。

这时 RocketMQ 不知道该提交还是回滚。

于是它会问生产者:

你刚才那条消息对应的本地事务到底成功了吗?

这个过程叫:

事务回查

9. 什么是事务回查?

这是事务消息里最容易懵的地方。

假设流程走到这里:

1. 半消息发送成功 2. 订单系统创建订单成功 3. 订单系统准备告诉 RocketMQ:提交消息 4. 结果订单系统突然宕机了

RocketMQ 此时很尴尬:

我这里有一条半消息 但是我不知道订单到底创建成功没有

怎么办?

RocketMQ 会过一段时间主动问订单系统:

这条消息对应的订单,到底有没有创建成功?

订单系统就去查数据库:

查一下 orderId = 10001 的订单是否存在

如果存在:

返回 COMMIT

如果不存在:

返回 ROLLBACK

这就是事务回查。


10. 用订单例子完整串起来

你发起下单:

用户点击下单

RocketMQ 事务消息流程:

第一步:订单系统发送半消息 消息内容:订单 10001 创建成功 但是消费者暂时看不到 第二步:订单系统执行本地事务 往订单表插入订单 10001 第三步:判断本地事务结果 如果订单插入成功: 提交消息 COMMIT 库存系统可以消费消息 如果订单插入失败: 回滚消息 ROLLBACK 库存系统看不到消息 如果订单系统宕机,不知道结果: RocketMQ 后面回查订单系统 订单系统查数据库 有订单 -> COMMIT 没订单 -> ROLLBACK

11. 小白版流程图

订单系统 | | 1. 发送半消息 v RocketMQ | | 2. 先保存,但不投递 | 订单系统 | | 3. 创建订单到数据库 v MySQL | | 4. 判断订单是否创建成功 | |--- 成功 ---> 告诉 RocketMQ 提交消息 ---> 消费者可以消费 | |--- 失败 ---> 告诉 RocketMQ 回滚消息 ---> 消费者看不到 | |--- 未知 ---> RocketMQ 后面回查订单系统

12. 事务消息解决了什么?没解决什么?

它解决的是:

生产者本地事务 和 MQ 消息发送 的一致性

比如:

订单创建成功,消息一定尽量发出去 订单创建失败,消息不要被消费

但它不解决:

消费者一定消费成功

消费者消费失败怎么办?

还是靠:

消费失败重试 幂等 死信队列 人工补偿

你之前问的“生产者消息发送出去了,消费者已经完全消费消息了”,这件事不是事务消息直接保证的。RocketMQ 里 Producer、Consumer、Topic、Group、Broker 的关系是:Producer 把消息发到 Broker,Consumer 再根据 Topic 和 Group 去消费消息。


13. 为什么不是直接用数据库事务包住 MQ 发送?

你可能会想:

@TransactionalpublicvoidcreateOrder(){orderService.saveOrder();rocketMQTemplate.convertAndSend("order_topic:create","订单创建成功");}

这样不就行了吗?

不完全行。

因为数据库事务只能管数据库,管不了 RocketMQ。

比如:

数据库事务提交成功了 但是 MQ 发送失败了

数据库没法自动回滚,因为事务已经提交了。

或者:

MQ 发送成功了 但是数据库事务回滚了

消息已经发出去了,消费者可能已经开始处理了。

所以:

数据库事务不能天然保证 MQ 消息一致性

RocketMQ 才引入事务消息。


14. 代码大概长什么样?

你不用现在完全看懂代码,先看注释。

发送事务消息:

rocketMQTemplate.sendMessageInTransaction("order_topic:create",MessageBuilder.withPayload(orderMessage).build(),orderMessage);

然后写一个事务监听器:

@Component@RocketMQTransactionListenerpublicclassOrderTransactionListenerimplementsRocketMQLocalTransactionListener{/** * 执行本地事务 * 这里通常写:创建订单、写数据库 */@OverridepublicRocketMQLocalTransactionStateexecuteLocalTransaction(Messagemessage,Objectarg){try{// 1. 执行本地事务:创建订单orderService.createOrder(arg);// 2. 本地事务成功,提交消息returnRocketMQLocalTransactionState.COMMIT;}catch(Exceptione){// 3. 本地事务失败,回滚消息returnRocketMQLocalTransactionState.ROLLBACK;}}/** * 事务回查 * 当 RocketMQ 不知道本地事务结果时,会调用这里 */@OverridepublicRocketMQLocalTransactionStatecheckLocalTransaction(Messagemessage){// 1. 根据订单ID查询数据库booleanorderExists=orderService.exists(message);// 2. 如果订单存在,说明本地事务成功if(orderExists){returnRocketMQLocalTransactionState.COMMIT;}// 3. 如果订单不存在,说明本地事务失败returnRocketMQLocalTransactionState.ROLLBACK;}}

你目前只要看懂这两个方法:

executeLocalTransaction:执行本地事务 checkLocalTransaction:事务回查

15. 再用一句话总结

事务消息就是:

先把消息发到 RocketMQ,但先不让消费者看到; 然后执行本地数据库事务; 如果数据库事务成功,就提交消息,让消费者消费; 如果数据库事务失败,就回滚消息,不让消费者消费; 如果中间状态不确定,RocketMQ 后面会回查生产者。

最关键的是这句话:

事务消息保证的是“生产者本地事务成功后,消息才对消费者可见”。

不是保证消费者一定消费成功。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/3 1:14:18

FSGlove手部动作捕捉系统:IMU阵列与DiffHCal校准技术解析

1. FSGlove系统概述手部动作捕捉技术正在经历一场从实验室走向产业化的革命。作为一名长期从事动作捕捉系统开发的工程师,我见证了从早期基于光学标记的笨重方案,到如今轻量化IMU设备的演进过程。FSGlove的出现,标志着这一领域在精度与实用性…

作者头像 李华
网站建设 2026/6/3 1:09:37

为什么很多硬件工程师工作10年,能力却只增长了2年?

在硬件研发行业,经常会出现一种耐人寻味的现象: 有的人工作三五年后,就能独立负责复杂项目,完成系统设计、故障分析和技术决策; 而有的人工作十几年,依然停留在“照着前人的图纸修改参数”的阶段。 两者最大的差别,往往并不在学历、智商或者工作时长,而在于一个被很…

作者头像 李华