下单需要同时扣库存、用优惠券和创建支付单,跨服务事务怎么保证最终一致?
我的判断
下单跨库存、优惠券和支付时,我会用本地事务加可靠事件/Saga,让每个服务幂等执行并提供补偿,不追求长时间全局锁。
订单服务先创建 PENDING 订单和 outbox 事件并同事务提交。库存服务收到事件后按订单 ID 幂等预留库存,优惠券服务预占券;每一步成功/失败都发结果事件,订单状态机汇总后决定进入待支付或取消。
PENDING -> STOCK_RESERVED -> COUPON_RESERVED -> WAIT_PAY
-> FAILED -> release stock / release coupon -> CANCELLED
补偿不是数据库 rollback。库存释放、优惠券返还都可能失败,所以也要幂等、重试、死信和人工处置。支付属于外部系统,超时后先查单;已扣款但订单取消要进入退款流程,不能简单反向调用。
消息链路使用 outbox/CDC,消费按 eventId 幂等,状态迁移带版本条件。后台扫描长时间停留在中间态的订单并补发事件,对订单、库存预留、券占用和支付流水做对账。
如果业务规模小且所有表在同库,本地事务更可靠简单;不会为了“微服务化”主动引入 Saga。
容易答偏踩坑误区
- 把补偿理解成必定成功的反向操作。 补偿本身也需要重试和人工兜底。
- 消息发出与本地事务分离。 容易出现订单已提交但事件没发。
- 状态机允许任意覆盖。 迟到事件会把终态订单改回中间态。