ARTICLE / 3 MIN READ

订单支付库存的一致性如何落地

用状态机、Outbox 和补偿任务处理跨服务写入,避免把分布式事务变成不可调试的黑盒。

订单、支付和库存通常属于不同服务,却共同决定用户是否成功购买。跨服务一致性不等于所有操作都同步提交,而是让每个状态变化可追踪、可重试、可补偿。

先定义业务状态机

订单待支付 -> 支付处理中 -> 支付成功 -> 待发货
       |           |             |
       v           v             v
    已取消      支付失败       退款中

每次转换都要校验前置状态、操作者和幂等键。支付回调重复到达时,只有第一次能把待支付改成支付成功,后续请求返回已处理。

Outbox 保证事件不丢

业务表和 outbox 表在同一个本地事务中提交。事务成功后,由后台投递器把 outbox 事件发送到消息队列;发送成功再标记已发送。

本地事务:
  UPDATE orders ...
  INSERT INTO outbox(event_id, aggregate_id, payload)
提交
后台投递 -> MQ -> 消费者 -> 幂等处理

这样即使服务在提交后立即崩溃,事件仍然留在 outbox 中可继续投递。投递器要有锁、批量发送和失败退避。

Saga 和补偿

库存预扣成功但支付失败时,需要执行释放库存的补偿动作。补偿不是简单地“再写一次”,要检查原操作是否确实成功,并记录补偿结果。

每个步骤都保存业务流水和关联的 saga ID,最终形成一条可审计链路。补偿失败进入人工处理队列,不能无限重试把下游打满。

事务消息的边界

两阶段提交会增加锁持有和协调器复杂度,适合资源少、强一致要求高且延迟可接受的场景。多数互联网订单更适合本地事务加可靠事件,再用状态机和对账保证收敛。

对账比“理论一致”更重要

每天按订单号、支付流水号和库存流水号做三方对账,发现“支付成功但订单未变更”“订单成功但库存未扣减”等差异,自动生成补偿任务。对账结果和修复动作必须留下审计记录。

分布式一致性的终点不是所有服务永远同时成功,而是任何中间状态都有出处、下一步和可验证的恢复路径。

GITHUB DISCUSSION

评论与回复

评论保存在 GitHub Discussions,登录 GitHub 后即可参与,发布和回复都在本页完成。

评论区进入视口后自动加载。