WEBKT

TCC模式实战:订单系统中的Try/Confirm/Cancel映射与一致性挑战

40 0 0 0

最近在重构公司的电商核心链路,TCC分布式事务模式又被提上了议程。说实话,TCC这三个字母念起来简单,但真要在订单、库存、积分、优惠券这几个核心系统里落地,里面的坑和细节真不少。

很多文章喜欢讲理论,咱们今天直接上场景:用户下单,系统需要依次完成:创建订单、扣除库存、扣除积分、发放优惠券。 这是一个典型的“强一致性”业务诉求,如果用最终一致性方案,库存超卖或者积分扣了订单没创建成功,用户投诉绝对会让你头疼。

下面拆解一下,TCC的三个阶段在这些业务操作中到底长什么样。

1. Try 阶段:资源预留与检查

Try阶段的核心不是直接执行业务,而是**“做戏做全套”的预备工作**。

  • 订单系统 (Order Service):
    • 并不是真的生成最终订单,而是创建一条状态为 Trying 的订单记录。这行记录的存在,就是一种“占位”。
    • 关键点: 检查用户状态是否正常,黑名单拦截等。
  • 库存系统 (Stock Service):
    • 这里最容易出错。不是直接扣减 quantity = quantity - 1
    • 而是执行冻结操作:frozen = frozen + 1,可用库存保持不变。
    • 目的: 防止Try阶段成功了,但后续Confirm失败,导致库存被“凭空”扣减。预留了资源,如果事务取消,再把冻结的加回来。
  • 积分系统 (Point Service):
    • 同理,不是直接扣积分,而是冻结积分。
    • usable_points = usable_points (不变), frozen_points = frozen_points + 100
  • 优惠券系统 (Coupon Service):
    • 将优惠券状态置为 LOCKED (锁定),防止被其他人领走或使用。

总结: Try阶段干的都是“脏活累活”,主要是资源检查和预留,保证进入下一阶段前,资源是够用的且被锁住了。

2. Confirm 阶段:提交业务

如果Try阶段所有参与方都返回成功,TC(事务协调器)就会发起Confirm。

  • 订单系统:Trying 状态的订单更新为 Normal (正常) 或 Paid (已支付)。
  • 库存系统: 真正的扣减库存。available = available - 1,同时将之前的 frozen = frozen - 1
  • 积分系统: 真正的扣减积分。usable = usable - 100frozen = frozen - 100
  • 优惠券系统: 将优惠券状态从 LOCKED 更新为 USED (已使用)。

注意: Confirm阶段要求幂等性。如果网络抖动导致Confirm重试,不能重复扣款。

3. Cancel 阶段:回滚/补偿

如果Try阶段任意一个环节失败,或者Confirm阶段执行了一半挂了,TC会触发Cancel。

  • 订单系统:Trying 状态的订单直接删除,或者标记为 Cancelled
  • 库存系统: 解除冻结。frozen = frozen - 1 (把之前Try预留的还回去)。
  • 积分系统: 解除冻结。frozen = frozen - 100
  • 优惠券系统:LOCKED 状态的优惠券改回 AVAILABLE (可用)。

注意: Cancel阶段同样要求幂等性,且要处理“空回滚”和“悬挂”问题(即Try没执行完,Cancel先执行了)。


可能遇到的数据一致性挑战

在实际落地中,TCC面临的挑战主要集中在以下几点:

  1. 悬空事务 (Hanging Transaction):

    • 场景: Cancel接口因为网络拥堵,比Try请求先到达了事务协调器并执行了。此时数据库里没有Try阶段预留的数据,Cancel执行了空操作。等Try请求到达并执行成功后,事务就卡住了,资源一直被锁定。
    • 对策: Cancel执行时,检查是否有对应的Try记录,如果没有,可能是空回滚,需要记录日志或插入一条空的Try记录以抵消。
  2. 幂等性控制 (Idempotency):

    • 场景: Confirm阶段扣库存成功了,但因为网络超时,TC重试了Confirm请求,导致库存被扣了两次。
    • 对策: 每个事务分支需要一个全局唯一的事务ID(XID)。在业务表中记录状态,或者利用Redis/数据库唯一索引去重。执行前先查一下是否已经处理过。
  3. Try阶段的资源占用时间:

    • 场景: 如果用户下单后一直不支付,Try阶段冻结的库存和积分会一直占用,导致其他用户买不到商品。
    • 对策: 需要引入定时任务,扫描长时间处于 Trying 状态的事务,自动触发Cancel操作,释放资源。
  4. 空回滚 (Null Rollback):

    • 场景: Try阶段还没执行完,事务就失败了,直接执行Cancel。此时Try可能还没插入数据,Cancel操作找不到数据。
    • 对策: Cancel时,如果发现没有对应的Try记录,直接返回成功即可,不需要报错。

TCC模式虽然代码侵入性强,需要开发大量的补偿接口,但它能保证强一致性,是处理资金、库存等核心资源最稳妥的方案之一。如果业务场景允许一定程度的数据延迟,其实消息队列+本地事务表的方案会更轻量。

码农老张 TCC分布式事务订单系统设计数据一致性

评论点评