TCC事务是一种常见的分布式事务机制。
在上文说到,通过可靠消息队列来保证事务的最终一致性,虽然能保证最终结果的相对可靠,开发过程也相对比较简单,但是却始终有那么一段时间,数据是不一致的,而且这个过程也是透明的,也就是说,整个流程上来说,是没有任何隔离性可言的,
比如秒杀活动,这种情况下,使用可靠消息队列实现,很容易出现超卖的现象发生,这对于业务层面来说,是基本不可能接受的。
为了不出现这种情况,后续的发展中,出现了很多的事务性强,且具有隔离性的,性能也很强的事务方案。
本文讲述的就是其中之一,非常经典的方案TCC。
TCC事务原理
TCC机制通过将事务分为三个阶段(Try、Confirm、Cancel)来保证事务的一致性。
- Try阶段,服务会尝试执行事务,说白了就是预留和锁定资源,但是不会将执行结果提交到数据库中;
- Confirm阶段,服务会将Try阶段中的执行结果提交到数据库中;
- Cancel阶段,服务会撤销Try阶段中的操作,将数据库状态恢复到事务开始前的状态。
这三个阶段是TCC事务的三个必要流程。
TCC事务实现方案
这个时候举一个例子,还是拿在可靠消息队列那篇文章中的例子来用吧,如下:
- 有一个分布式事务操作。
- 在事务中有一个操作,分别要访问A服务,B服务,C服务。
- A服务是本地服务,B服务和C服务是远程调用的服务。
那么,在这个案例中,如果我们要通过TCC方案实现这个案例,是怎么样的一个流程呢?
- 首先第一步,虽然A服务是本地服务,但是依然会去执行一个try操作,如果执行失败了,因为是本地事务,所以事务可以直接回滚。
- 如果执行A服务的try阶段成功了,那么接下来会执行B服务的try阶段,如果执行B服务的try阶段失败了,因为B服务是远程调用服务,那么就需要执行A服务和B服务的cancel方法,释放预留和锁定的资源。
- 如果执行B服务成功了,那么就开始执行C服务的try阶段,如果执行失败了,那么也会去执行A服务,B服务以及C服务的cancel方法。
- 执行完A服务,B服务以及C服务try阶段后,开始执行A服务,B服务以及C服务的Confirm阶段。
- 一旦哪个服务的Confirm阶段执行失败,那么就要执行A服务,B服务以及C服务的cancel阶段了。
- 如果哪个服务的Cancel阶段执行失败了,可以进行重试操作,或者人工介入补偿。
这就是TCC模式的一个大概流程,当然,我们在实际使用TCC模式的时候,还是会有一些问题。
为什么需要try阶段,为什么try阶段不直接confirm阶段合并?
如果我们在开发的过程中,如上面的案例中所示,一个操作,需要三个服务的接入,那么我们如果执行执行了A服务,B服务以及C服务,那么一旦有一个服务,比如说扣钱这个操作,由于余额不足,而导致服务执行失败了,那么就需要去回滚其他已经执行成功了的服务的数据。
但是try阶段独立出来了,就会有如下的优点:
- try阶段可以进行校验资源,并且判断资源是否充足,不充足或不符合条件的情况下可以直接拒绝后续的操作。
- try阶段不会去直接修改资源,只会锁定和预留资源,如果需要回滚,回滚逻辑也相对简单。
- try阶段不直接修改资源,避免了并发的问题产生。
try阶段的预留和锁定资源是如何操作的?
实际的开发过程中,try,confirm,cancel操作,都是需要我们开发人员编写的。
那么try阶段是如何预留和锁定资源的呢?在实际的开发过程中,我们一般都是有以下几种办法处理的:
- 分布式锁。在try阶段,我们通过分布式锁锁定资源,到了confirm阶段会将这个分布式锁释放掉。
- 数据库中乐观锁。在try阶段,可以使用版本号的形式。
在try阶段,一个服务执行try失败了,其他服务就一定要cancel操作吗?
提出这个问题,是因为如果try阶段失败了执行cancel操作,confirm失败了也执行cancel操作,那这样try阶段和confirm阶段的cancel都混在一起,很难区分。
所以一般在try阶段如果有一个服务执行失败了,那么所有的服务除了可以执行cancel操作以外,其实也可以单独写一个compensate操作:当某个服务的try操作失败时,可以采用compensate操作来进行数据的修复,以确保数据的一致性。
compensate操作通常是通过执行与try操作相反的操作来实现的,例如释放预留的资源,以及释放锁定的资源。
TCC的注意事项
- try,confirm,cancel这三个阶段,每一个阶段的操作都应该保证幂等性。
- 在使用TCC模式的时候,需要考虑网络故障、系统崩溃等,采取相应的措施来保证系统的容错性和可用性,比如说可以采用重试、超时机制来确保远程服务的执行,或者采用消息队列等机制来实现异步执行,从而避免因为远程服务的故障而导致事务失败。
- 主事务和分支事务,都应该写到日志中,以便后续进行数据补偿和维护。
总结
TCC模式,业内已经有很成熟的开源框架实现,比如阿里的Seata,就支持了很多的分布式事务方案。如果有兴趣的话,可以去学习一下Seata。