一、简介
2007年,Pat Helland发表了一篇名为《Life beyond Distributed Transactions: an Apostate’s Opinion》的论文,提出了 TCC(Try-Confirm-Cancel) 的概念。
两阶段提交(2PC)和三阶段提交(3PC)并不适用于并发量大的业务场景。TCC事务机制相比于2PC、3PC,不会锁定整个资源,而是通过引入 补偿机制 ,将资源转换为业务逻辑形式,锁的粒度变小。
TCC的核心思想是: 针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作 ,分为三个阶段:
- Try: 这个阶段对各个服务的资源做检测以及对资源进行锁定或者预留;
- Confirm : 执行真正的业务操作,不作任何业务检查,只使用Try阶段预留的业务资源,Confirm操作要求具备幂等设计,Confirm失败后需要进行重试;
- Cancel: 如果任何一个服务的业务方法执行出错,那么这里就需要进行补偿,即执行回滚操作,释放Try阶段预留的业务资源 ,Cancel操作要求具备幂等设计,Cancel失败后需要进行重试。
举个例子,电商系统中有两个服务:订单服务A、库存服务B:
对外提供服务时,必须接受一些不确定性,即对服务A/B的一次调用仅是一个临时性操作,服务消费方保留了后续的取消权。
如果消费方认为全局事务应该rollback,它会要求取消之前的临时性操作;如果消费方认为全局事务应该commit时,它会进行的一个确认操作。
二、TCC的执行
TCC将一次事务操作分为三个阶段:Try、Confirm、Cancel,我们通过一个订单/库存的示例来理解。假设我们的分布式系统一共包含4个服务:订单服务、库存服务、积分服务、仓储服务,每个服务有自己的数据库,如下图:
2.1 Try
Try阶段一般用于锁定某个资源,设置一个预备状态或冻结部分数据。对于示例中的每一个服务,Try阶段所做的工作如下:
- 订单服务:先置一个中间状态“UPDATING”,而不是直接设置“支付成功”状态;
- 库存服务:先用一个冻结库存字段保存冻结库存数,而不是直接扣掉库存;
- 积分服务:预增加会员积分;
- 仓储服务:创建销售出库单,但状态是UNKONWN。
2.2 Confirm
根据Try阶段的执行情况,Confirm分为两种情况:
- 理想情况下,所有Try全部执行成功,则执行各个服务的Confirm逻辑;
- 部分服务Try执行失败,则执行第三阶段——Cancel。
Confirm阶段一般需要各个服务自己实现Confirm逻辑:
- 订单服务:confirm逻辑可以是将订单的中间状态变更为PAYED-支付成功;
- 库存服务:将冻结库存数清零,同时扣减掉真正的库存;
- 积分服务:将预增加积分清零,同时增加真实会员积分;
- 仓储服务:修改销售出库单的状态为已创建-CREATED。
Confirm阶段的各个服务本身可能出现问题,这时候一般就需要TCC框架了(比如ByteTCC,tcc-transaction,himly),TCC事务框架一般会记录一些分布式事务的活动日志,保存事务运行的各个阶段和状态,从而保证整个分布式事务的最终一致性。
2.3 Cancel
如果Try阶段执行异常,就会执行Cancel阶段。比如:对于订单服务,可以实现的一种Cancel逻辑就是:将订单的状态设置为“CANCELED”;对于库存服务,Cancel逻辑就是:将冻结库存扣减掉,加回到可销售库存里去。
许多公司为了简化TCC的使用,通常会将一个服务的某个核心接口拆成两个,比如库存服务的扣减库存接口,拆成两个子接口:①扣减接口 ②回滚扣减库存接口,由TCC框架来保证当某个接口执行失败后去执行对应的rollback接口。
三、总结
从正常的流程上讲,TCC仍然是一个两阶段提交协议。但是,在执行出现问题的时候,有一定的自我修复能力,如果任何一个事务参与者出现了问题,协调者可以通过执行逆操作来取消之前的操作,达到最终的一致状态(比如冲正交易、查询交易)。
从TCC的执行流程也可以看出,服务提供方需要提供额外的 补偿逻辑 ,那么原来一个服务接口,引入TCC后可能要改造成3种逻辑:
- Try:先是服务调用链路依次执行Try逻辑;
- Confirm:如果都正常的话,TCC分布式事务框架推进执行Confirm逻辑,完成整个事务;
- Cancel:如果某个服务的Try逻辑有问题,TCC分布式事务框架感知到之后就会推进执行各个服务的Cancel逻辑,撤销之前执行的各种操作。
注意:在设计TCC事务时,接口的Cancel和Confirm操作都必须满足幂等设计。
3.1 框架选型
TCC框架的可供选择余地比较少,目前相对比较成熟的是阿里开源的分布式事务框架seata(Seata并不完全是一个TCC事务框架),这个框架是经历过阿里生产环境的大量考验,同时也支持dubbo、spring cloud。
3.2 优点
跟2PC比起来,实现以及流程相对简单了一些,但数据的一致性比2PC也要差一些,当然性能也可以得到提升。
3.3 缺点
TCC模型对业务的侵入性太强,事务回滚实际上就是自己写业务代码来进行回滚和补偿,改造的难度大。一般来说支付、交易等核心业务场景,可能会用TCC来严格保证分布式事务的一致性,要么全部成功,要么全部自动回滚。这些业务场景都是整个公司的核心业务有,比如银行核心主机的账务系统,不容半点差池。
但是,在一般的业务场景下,尽量别没事就用TCC作为分布式事务的解决方案,因为自己手写回滚/补偿逻辑,会造成业务代码臃肿且很难维护。