说说 RabbitMQ 的事务机制?
结论:AMQP 协议提供
txSelect/txCommit/txRollback三个命令,能保证"一批发布要么全部进入 Broker、要么全部不进入"。但它是同步阻塞的,每条消息都要等一次往返,吞吐会掉一到两个数量级;而且它管不了路由失败、Broker 宕机、消费端处理失败。所以生产环境用发布确认(Publisher Confirm)+ 手动 ack 替代它。
一、事务的执行过程
Producer Broker
│ txSelect │ 开启事务模式(信道级)
├───────────────────────────>│
│ basicPublish(msg1) │ 暂存,不投递
├───────────────────────────>│
│ basicPublish(msg2) │
├───────────────────────────>│
│ txCommit │ 原子提交,两条一起入队
├───────────────────────────>│
│ <──── tx.commit-ok ───────┤ 同步等待,期间信道阻塞
│
│ 若任一步失败 → txRollback → 全部丢弃
└───────────────────────────>│
事务是信道级的:txSelect 之后这个 Channel 上的所有发布都在事务里,直到 commit 或 rollback。事务与 confirm 模式互斥,同一个 Channel 不能同时开启。
二、三种可靠性机制的分工
| 机制 | 保证什么 | 同步/异步 | 相对吞吐 | 生产是否使用 |
|---|---|---|---|---|
| 事务 tx | 一批发布原子入 Broker | 同步,每步等响应 | 很低(基准的几十分之一) | 基本不用 |
| 发布确认 confirm | 消息被交换机/队列接收 | 异步回调 | 高 | 常用 |
| 消费手动 ack | 业务处理成功后才确认 | 异步 | 高 | 必用 |
三者解决的是不同环节:tx/confirm 管"发出去没有",ack 管"消费成功没有"。事务并不能替代另外两个。
三、代码对比
// 事务写法:同步、阻塞
channel.txSelect();
try {
channel.basicPublish("order.ex", "order.created", null, body1);
channel.basicPublish("order.ex", "order.created", null, body2);
channel.txCommit(); // 两次发布一起生效
} catch (IOException e) {
channel.txRollback(); // 一起失效
}
// confirm 写法:异步批量,吞吐高
channel.confirmSelect();
channel.addConfirmListener(
(seq, multiple) -> markDone(seq, multiple), // 成功
(seq, multiple) -> retryLater(seq, multiple)); // 失败,走补偿
for (byte[] body : batch) {
channel.basicPublish("order.ex", "order.created",
MessageProperties.PERSISTENT_TEXT_PLAIN, body);
}
confirm 的关键差异是"不等结果就继续发",结果通过回调汇总;tx 则是"发一条等一条",网络往返次数与消息数成正比。
四、事务的三个局限
| 局限 | 说明 |
|---|---|
| 管不到路由 | 消息成功进入 Broker 但没有队列匹配,事务照样提交成功,消息被丢弃 |
| 管不到存储 | Broker 收到并提交后宕机,若消息未持久化、队列无副本,依然会丢 |
| 管不到消费 | 消费端的失败与重复,事务机制完全不参与 |
所以"用事务保证不丢消息"是伪命题:它只把"发送失败"这一种情况排除掉了,其余环节仍要 confirm 的 mandatory/return、持久化、手动 ack 来守。
五、常见追问
- 事务为什么这么慢? 每个命令都要同步等 Broker 的
tx.commit-ok,且事务期间信道被独占,无法流水线发送;Broker 侧还要为事务维护状态。 - 事务能和 confirm 一起用吗? 不能。两者都改变信道的确认模式,同时开启会报错。
- 消费端有事务吗? AMQP 的 tx 只作用于发布与确认,消费端没有"处理事务"的概念;业务要么手动 ack,要么用数据库事务 + 幂等来保证。
- 什么场景还值得用事务? 极低频、强一致、消息量很小的管理类操作,比如一次只发一条配置变更;高吞吐业务没有理由用它。
小结
- RabbitMQ 事务保证的是"一批发布原子进入 Broker",能力边界很窄。
- 同步阻塞导致吞吐骤降,是它被 confirm 取代的根本原因。
- 路由失败、存储丢失、消费失败都不在事务覆盖范围内。
- 生产标配是 confirm + mandatory + 持久化 + 手动 ack + 幂等。