事务消息与最终一致性:订单、支付、库存如何用 MQ 串起来
事务消息解决的是本地事务与消息发送之间的一致性问题,最终一致性则依赖幂等、补偿和对账收敛。
事务消息与最终一致性:订单、支付、库存如何用 MQ 串起来
业务系统里最经典的问题是:数据库事务提交成功了,但消息发送失败怎么办?
比如订单支付成功后,本地订单库已经更新为 PAID,但 OrderPaid 消息没有发出去。库存、履约、报表都收不到事件,系统就不一致了。
Rendering diagram...
本地事务 + 消息的矛盾
数据库事务和 MQ 发送不是同一个事务资源。除非引入分布式事务,否则无法天然保证两者同时成功或同时失败。
常见解决方案有两类:事务消息和 Outbox Pattern。
RocketMQ 事务消息
RocketMQ 事务消息的基本思路是先发送半消息,再执行本地事务,最后提交或回滚消息。
Rendering diagram...
如果 Producer 在提交二次确认前挂掉,Broker 可以回查本地事务状态,再决定提交或回滚消息。
Outbox Pattern
Outbox 是另一种通用方案。在同一个本地事务里写业务表和消息表。
BEGIN;
UPDATE orders
SET status = 'PAID'
WHERE id = 10001;
INSERT INTO outbox_events(event_id, event_type, payload, status)
VALUES ('evt_10001', 'OrderPaid', '{"order_id":10001}', 'NEW');
COMMIT;
之后由后台任务或 CDC 把 outbox_events 投递到 MQ。这样至少保证业务状态和待发送事件同时落库。
消费端最终一致
消息发出去后,下游也可能失败。库存服务可能处理失败,履约服务可能超时,报表服务可能写入失败。
所以消费者要做到:
- 幂等。
- 可重试。
- 失败进入死信。
- 有补偿任务。
- 有对账机制。
小结
事务消息解决的是“本地事务和消息发送”的一致性。最终一致性解决的是“多个服务最终状态收敛”的问题。
在订单、支付、库存场景里,可靠消息、幂等消费、补偿任务和对账缺一不可。