善忘技术夹 Logo
善忘技术夹
架构与设计

跨微服务扣款怎么保一致?两阶段提交 2PC 与 TCC 分布式事务实战

用户下单,涉及三个微服务:

订单服务:创建订单,状态改为"待支付"
支付服务:扣用户余额
库存服务:扣减商品库存

订单创建成功、支付扣款成功,但库存扣减失败——用户的钱已经扣了,但库存没扣,订单超卖。

单机数据库里一个事务就能搞定。但在分布式环境下,一个事务跨越三个服务、三个数据库,MySQL 的 BEGIN / COMMIT 管不了。

分布式事务就是来解决这个问题的。

白话版

插图:2PC婚礼

两种主流方案,用两个生活场景来类比:

2PC(两阶段提交) — 像婚礼上的交换戒指仪式:

第一阶段(准备阶段 Prepare):
  司仪问:"新郎,你愿意娶新娘为妻吗?" -> 新郎回答:"我愿意"
  司仪问:"新娘,你愿意嫁给新郎吗?" -> 新娘回答:"我愿意"

第二阶段(提交阶段 Commit):
  双方都同意 -> 司仪宣布:"交换戒指,正式结为夫妻!"(事务提交)
  只要有一方说"不愿意" -> 婚礼中断(事务回滚)

TCC(Try-Confirm-Cancel) — 像预订酒店房间:

1. Try(预留阶段):
   调用酒店接口,冻结房间(预留资源),但不正式扣款。

2. Confirm(确认阶段):
   客人确认入住,正式扣款消费(真正的业务操作)。

3. Cancel(取消阶段):
   客人行程取消,解冻预留的房间(释放资源)。

2PC 与 TCC 深度对比

维度2PC (Two-Phase Commit)TCC (Try-Confirm-Cancel)
实现层级数据库/XA 协议层 (底层)业务应用代码层 (高层)
锁定资源锁定数据库记录 (长锁,性能低)仅冻结/预留业务资源
性能吞吐低 (强同步阻断)高 (非阻塞高并发)
业务入侵性无侵入 (由 DB/框架管理)高侵入 (每个接口都要写 Try/Confirm/Cancel 3个方法)
适用场景传统单体拆分少、对性能要求低的系统互联网高并发微服务、核心支付交易

TCC 异常处理三大坑

在实际微服务生产环境中,TCC 模式必须处理以下三种网络异常情况:

1. 空回滚 (Empty Cancel)

  • 现象Try 请求因网络丢包压根没收到,但事务协调器超时触发了 Cancel
  • 解决:在 Cancel 方法中识别 Try 是否执行过,若未执行,直接返回成功。

2. 悬挂 (Hanging)

  • 现象Try 因网络拥堵延迟到达,而 Cancel 已经先执行并完成。
  • 解决:在 Try 执行时检查 Cancel 是否已执行,若已 Cancel,则拒绝执行 Try

3. 幂等性 (Idempotency)

  • 现象:网络重试导致 ConfirmCancel 被重复调用。
  • 解决:使用本地事务记录 + 唯一事务 ID 进行去重防重。

小结

  • 2PC 适合简单的数据库层分布式事务,但性能较低。
  • TCC 适合微服务高性能核心场景,虽然业务代码量翻倍,但并发能力极强。

关注「善忘技术夹」全媒体矩阵

扫描上方宣传海报二维码,第一时间获取最新技术文章、开源项目与免安装小程序体验。

善忘技术夹 微信公众号宣传图
微信公众号 (扫码关注)
善忘技术夹 微信小程序宣传图
微信小程序 (扫码即用)