什么是 TCC 事务

TCC 事务(Try-Confirm-Cancel)是一种补偿型分布式事务处理模式,由于采用了无锁设计和自定义业务逻辑的方式,TCC 通常适用于高并发的分布式系统。

TCC 是 TryConfirmCancel 三个词的缩写,它将事务分为三个阶段执行,以确保在分布式系统中达到最终一致性

1 . Try(尝试)阶段:

在这个阶段,系统会检查事务执行的前提条件,预留必需的业务资源,但不实际执行事务操作。

例如,在一个订单服务中,Try 阶段可能包括检查商品库存是否充足,并对库存进行预留,但不实际扣减库存。

2 . Confirm(确认)阶段:

如果 Try 阶段所有参与者的操作都成功,并且决定提交事务,系统会进入 Confirm 阶段。在这个阶段,之前预留的资源会被正式使用或变更。

继续上面的例子,这里会真正扣减商品库存,完成订单创建。

3 . Cancel(取消)阶段:

如果在 Try 阶段之后,有任何参与者操作失败,或者决策需要回滚事务,系统会进入 Cancel 阶段。这个阶段会撤销 Try 阶段所做的所有预留操作,释放资源,使系统状态回到事务开始前。

在订单服务的例子中,如果支付服务失败,Cancel 阶段会释放之前预留的库存。

Summary

TCC 事务模型要求业务操作被设计成可逆的,即每个 Try 操作都需要有一个对应的 Cancel 操作来补偿。TCC 事务的优势在于它不依赖于分布式锁,减少了事务处理的阻塞,提高了系统的吞吐量,但同时也增加了业务逻辑的复杂度,因为需要为每个操作设计相应的补偿逻辑。此外,TCC 事务模型更适合于短事务处理,对于长事务或涉及复杂业务逻辑的场景,实施难度会相应增加。

示例

假设我们的分布式系统一共包含 4 个服务:订单服务、库存服务、积分服务、仓储服务,每个服务有自己的数据库,如下图:

1. Try

Try 阶段一般用于锁定某个资源,设置一个预备状态或冻结部分数据。对于示例中的每一个服务,Try 阶段所做的工作如下:

  • 订单服务:先置一个中间状态 UPDATING,而不是直接设置“支付成功”状态;

  • 库存服务:先用一个冻结库存字段保存冻结库存数,而不是直接扣掉库存;

  • 积分服务:预增加会员积分;

  • 仓储服务:创建销售出库单,但状态是 UNKONWN

2. Confirm

根据 Try 阶段的执行情况,Confirm 分为两种情况:

  1. 理想情况下,所有 Try 全部执行成功,则执行各个服务的 Confirm 逻辑;
  2. 部分服务 Try 执行失败,则执行第三阶段——Cancel。

Confirm 阶段一般需要各个服务自己实现 Confirm 逻辑:

  • 订单服务:将订单的中间状态变更为支付成功-PAYED

  • 库存服务:将冻结库存数清零,同时扣减掉真正的库存;

  • 积分服务:将预增加积分清零,同时增加真实会员积分;

  • 仓储服务:修改销售出库单的状态为已创建-CREATED

Tip

Confirm 阶段的各个服务本身可能出现问题,这时候一般就需要 TCC 框架了(比如 ByteTCC,tcc-transaction,himly),TCC 事务框架一般会记录一些分布式事务的活动日志,保存事务运行的各个阶段和状态,从而保证整个分布式事务的最终一致性。

3. Cancel

如果 Try 阶段执行异常,就会执行 Cancel 阶段:

  • 订单服务:将订单的状态设置为 CANCELED

  • 库存服务:将冻结库存扣减掉,加回到可销售库存里去。

Tip

许多公司为了简化 TCC 的使用,通常会将一个服务的某个核心接口拆成两个,比如库存服务的扣减库存接口,拆成两个子接口:

  1. 扣减接口
  2. 回滚扣减库存接口

由 TCC 框架来保证当某个接口执行失败后,去执行对应的 rollback 接口。

TCC 和 2PC/3PC 有什么区别?

  • 2PC/3PC 依靠数据库的事务来实现,TCC 主要通过修改业务代码来实现。
  • 2PC/3PC 属于业务代码无侵入的,TCC 对业务代码有侵入。
  • 2PC/3PC 追求的是强一致性,在两阶段提交的整个过程中,一直会持有数据库的锁。TCC 追求的是最终一致性,不会一直持有各个业务资源的锁。

优缺点

优点

跟 2PC 比起来,实现以及流程相对简单了一些,但数据的一致性比 2PC 也要差一些,当然性能也可以得到提升。

缺点

TCC 模型对业务的侵入性太强,事务回滚实际上就是自己写业务代码来进行回滚和补偿,改造的难度大。一般来说支付、交易等核心业务场景,可能会用 TCC 来严格保证分布式事务的一致性,要么全部成功,要么全部自动回滚。这些业务场景都是整个公司的核心业务有,比如银行核心主机的账务系统,不容半点差池。

但是,在一般的业务场景下,尽量别没事就用 TCC 作为分布式事务的解决方案,因为自己手写回滚/补偿逻辑,会造成业务代码臃肿且难以维护。

应用场景

在电商促销活动中,使用 TCC 确保用户下单后的库存扣减、优惠券使用等操作的一致性。

框架选型

  1. ByteTCC : ByteTCC 是基于 Try-Confirm-Cancel(TCC)机制的分布式事务管理器的实现。相关阅读:关于如何实现一个 TCC 分布式事务框架的一点思考
  2. Seata :阿里巴巴开源的分布式事务解决方案。
  3. Hmily : 金融级分布式事务解决方案。

总结

从正常的流程上讲,TCC 仍然是一个两阶段提交协议。但是,在执行出现问题的时候,有一定的自我修复能力,如果任何一个事务参与者出现了问题,协调者可以通过执行逆操作来取消之前的操作,达到最终的一致状态(比如冲正交易、查询交易)。

从 TCC 的执行流程也可以看出,服务提供方需要提供额外的补偿逻辑,那么原来一个服务接口,引入 TCC 后可能要改造成 3 种逻辑。

注意:在设计 TCC 事务时,接口的 Cancel 和 Confirm 操作都必须满足幂等设计。