什么是 TCC 事务
TCC 事务(Try-Confirm-Cancel)是一种补偿型分布式事务处理模式,由于采用了无锁设计和自定义业务逻辑的方式,TCC 通常适用于高并发的分布式系统。
TCC 是 Try、Confirm、Cancel 三个词的缩写,它将事务分为三个阶段执行,以确保在分布式系统中达到最终一致性。
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 分为两种情况:
- 理想情况下,所有 Try 全部执行成功,则执行各个服务的 Confirm 逻辑;
- 部分服务 Try 执行失败,则执行第三阶段——Cancel。
Confirm 阶段一般需要各个服务自己实现 Confirm 逻辑:
-
订单服务:将订单的中间状态变更为支付成功-PAYED;
-
库存服务:将冻结库存数清零,同时扣减掉真正的库存;
-
积分服务:将预增加积分清零,同时增加真实会员积分;
-
仓储服务:修改销售出库单的状态为已创建-CREATED。
Tip
Confirm 阶段的各个服务本身可能出现问题,这时候一般就需要 TCC 框架了(比如 ByteTCC,tcc-transaction,himly),TCC 事务框架一般会记录一些分布式事务的活动日志,保存事务运行的各个阶段和状态,从而保证整个分布式事务的最终一致性。
3. Cancel
如果 Try 阶段执行异常,就会执行 Cancel 阶段:
-
订单服务:将订单的状态设置为 CANCELED;
-
库存服务:将冻结库存扣减掉,加回到可销售库存里去。
Tip
许多公司为了简化 TCC 的使用,通常会将一个服务的某个核心接口拆成两个,比如库存服务的扣减库存接口,拆成两个子接口:
- 扣减接口
- 回滚扣减库存接口
由 TCC 框架来保证当某个接口执行失败后,去执行对应的 rollback 接口。
TCC 和 2PC/3PC 有什么区别?
- 2PC/3PC 依靠数据库的事务来实现,TCC 主要通过修改业务代码来实现。
- 2PC/3PC 属于业务代码无侵入的,TCC 对业务代码有侵入。
- 2PC/3PC 追求的是强一致性,在两阶段提交的整个过程中,一直会持有数据库的锁。TCC 追求的是最终一致性,不会一直持有各个业务资源的锁。
优缺点
优点:
跟 2PC 比起来,实现以及流程相对简单了一些,但数据的一致性比 2PC 也要差一些,当然性能也可以得到提升。
缺点:
TCC 模型对业务的侵入性太强,事务回滚实际上就是自己写业务代码来进行回滚和补偿,改造的难度大。一般来说支付、交易等核心业务场景,可能会用 TCC 来严格保证分布式事务的一致性,要么全部成功,要么全部自动回滚。这些业务场景都是整个公司的核心业务有,比如银行核心主机的账务系统,不容半点差池。
但是,在一般的业务场景下,尽量别没事就用 TCC 作为分布式事务的解决方案,因为自己手写回滚/补偿逻辑,会造成业务代码臃肿且难以维护。
应用场景
在电商促销活动中,使用 TCC 确保用户下单后的库存扣减、优惠券使用等操作的一致性。
框架选型
- ByteTCC : ByteTCC 是基于 Try-Confirm-Cancel(TCC)机制的分布式事务管理器的实现。相关阅读:关于如何实现一个 TCC 分布式事务框架的一点思考
- Seata :阿里巴巴开源的分布式事务解决方案。
- Hmily : 金融级分布式事务解决方案。
总结
从正常的流程上讲,TCC 仍然是一个两阶段提交协议。但是,在执行出现问题的时候,有一定的自我修复能力,如果任何一个事务参与者出现了问题,协调者可以通过执行逆操作来取消之前的操作,达到最终的一致状态(比如冲正交易、查询交易)。
从 TCC 的执行流程也可以看出,服务提供方需要提供额外的补偿逻辑,那么原来一个服务接口,引入 TCC 后可能要改造成 3 种逻辑。
注意:在设计 TCC 事务时,接口的 Cancel 和 Confirm 操作都必须满足幂等设计。
Reference