TCC事务的学习和理解
前段时间了解了下TCC事务的一些原理和应用,来谈一下自己的理解。TCC是分布式事务实现的一种方式,分为3个阶段 Try-/confirm/i-Cancel
基本概念
首先TCC是英文Try-/confirm/i-Cancel的缩写,它是解决分布式事务的一种机制,也是支付宝在解决柔性事务的一种方案。柔性事务这个名词第一次看到,所以又去看了一下概念,整理了一下大致是以下这么一种分类:
刚性事务
ACID:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)
CAP :
C(一致性)一致性是指数据的原子性,在经典的数据库中通过事务来保障,事务完成时,无论成功或回滚,数据都会处于一致的状态,在分布式环境下,一致性是指多个节点数据是否一致;
A(可用性)服务一直保持可用的状态,当用户发出一个请求,服务能在一定的时间内返回结果;
P(分区容忍性)在分布式应用中,可能因为一些分布式的原因导致系统无法运转,好的分区容忍性,使应用虽然是一个分布式系统,但是好像一个可以正常运转的整体; (数据库事务都是刚性事务,严格准守ACID原则,要求事务有强一致性)
柔性事务
base
BA: Basic Availability 基本业务可用性;
S: Soft state 柔性状态;
E: Eventual consistency 最终一致性;
可以看出,柔性事务相对于刚性事务,它并不要求强一致性,在业务上允许一定程度的延时、阻塞,但最终是一致的。 支付宝架构于技术中对柔性事务的描述为:
两阶段型 —> 参考分布式系统的事务处理
补偿型
异步确保型
最大努力通知型几种
重点来看下事务补偿型TCC事务。
何为TCC?
TCC相较于传统的事务机制,并不依赖于传统的资源管理,而是通过对业务的调度来实现分布式事务。因此,一个业务逻辑若要实现TCC事务控制,必须实现这3个业务逻辑:
Try:资源预留/锁定操作,是事务发起方调用服务提供方的try方法来锁定业务需要的所有资源。
/confirm/i:确认执行业务逻辑操作,这里使用的资源一定是Try阶段预留的业务资源。在TCC事务机制中认为,如果在try阶段能正常的预留资源,那么/confirm/i和cancel阶段一定能完整正确的提交。/confirm/i阶段也可以看成是对try阶段的一个补充,try+/confirm/i一起组成了一个完整的业务逻辑。在一篇文章中看到过这么一个说法,我觉得挺对的:TCC机制将传统事务机制中的业务逻辑一分为二,拆分后保留的部分即为初步操作(Try);而分离出的部分即为确认操作(/confirm/i),被延迟到事务提交阶段执行。
Cancel:取消执行业务逻辑。但这和普通的补偿性事务不一样的地方是,cancel阶段并没有真正的回滚业务(因为try阶段并没有真正执行业务),而是释放之前锁定的资源。我认为这样比传统的rollback代价要小,可以参考之前在网上看到的一篇文章中的股票交易的例子:Business Transactions, Compensation and the TryCancel/Confirm (TCC) Approach for Web Services
实际案例
网上看了很多案例来解释TCC,我根据自己的理解,结合前段时间新做的项目的交易场景来分析下TCC的可行性。
在汽车新零售项目中,客户通过二级经销商销售人员下单一辆奥迪Q5的过程中,会创建B2C交易订单,B2B采购订单,创建合同,锁定库存等一系列动作,其中涉及到多个服务的调用。
在这个场景中,新零售交易后台需要调用3个服务:订单服务,合同服务和库存服务。
TCC事务的学习和理解
首先在Try阶段预留资源:调用订单服务创建B2C和B2B订单(由于他们同库操作,所以2个订单的创建放在一个数据库事务中实现),这时的订单是一个中间状态(例如待创建);调用合同服务创建B2B和B2C合同,和订单服务一样,创建的合同也是出于一个中间状态;库存服务需要预留一个车辆,也就是锁定一个库存。需要注意的是,这里的服务都是分布式服务调用,所以要特别注意接口的幂等性和并发操作等问题。
若Try阶段执行成功,即所有的资源都预留成功,那就可以进入到/confirm/i阶段。订单服务更新订单状态,将中间状态更新为已创建待支付(这是一个业务状态);合同服务同理,将合同从中间状态更新为已签署;库存服务将锁定的库存真实减去。
若Try阶段预留资源失败,TCC事务管理器认为全局事务不能正确提交时,就会逐个执行初步操作(Try)指定的取消操作(Cancel),将初步操作(Try)已完成的事项全部撤回。
在实际项目中,我们可能会做延迟撤销:try阶段预留资源失败后,并不马上修改订单、合同和库存状态,而是通过定时任务扫描异常单或者超时处理等方式来达到最终一致性。举个例子:在try阶段订单和合同创建成功,达到带创建状态,而库存锁定失败;这是可以不必马上做cacel操作,可以业务上规定订单30分钟不进入正常状态即认为操作失败,系统可以通过跑定时任务来扫描这些异常单并作出相应的处理(定时补偿机制)。
思考
TCC在某种意义上来说和两阶段(2PC)提交非常类似,Try和prepare类似,/confirm/i相当于commit,cancel和rollback一样都是回滚动作,只不过TCC的两阶段提交是在业务层面的(需要开发者根据各自业务的特点来实现try-/confirm/i-cancel),2PC则更多用在数据库事务处理上,倾向于底层的实现和具体业务无关。
最后来思考一个问题,TCC是否适用于所有的业务场景?答案一定是否定的,没有一种方案是万能的。
首先,TCC事务机制要求必须实现Try confirm cancel3个接口,所以需要额外的开发资源;
其次,由于TCC的关键在于Try阶段的资源预留,当业务复杂度比较高的时候,就会产生很多的中间状态,预留的资源的成本会比较高,倍数增加了业务的复杂度,对性能也会有影响。
所以,在处理分布式事务的时候,还是具体情况具体分析,根据业务场景选择不同的事务处理机制,也可以多种机制混合使用(2PC,TCC,事务型消息队列,事务补偿等等),毕竟适合自己的才是最好的。