事务消息与最终一致性:订单、支付、库存如何用 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。这样至少保证业务状态和待发送事件同时落库。

消费端最终一致

消息发出去后,下游也可能失败。库存服务可能处理失败,履约服务可能超时,报表服务可能写入失败。

所以消费者要做到:

  • 幂等。
  • 可重试。
  • 失败进入死信。
  • 有补偿任务。
  • 有对账机制。

小结

事务消息解决的是“本地事务和消息发送”的一致性。最终一致性解决的是“多个服务最终状态收敛”的问题。

在订单、支付、库存场景里,可靠消息、幂等消费、补偿任务和对账缺一不可。

参考链接