5.3.2 TCC
TCC(Try、Confirm、Cancel)事务模型源于 Pat Helland 在论文《Life beyond Distributed Transactions: an Apostate’s Opinion》[1] 中提出的概念。TCC 引入了一种新的事务模型,允许业务层面自行定义事务,并由业务控制锁的粒度,解决复杂业务中跨表、跨库等大粒度资源锁定的问题。
如同 TCC 事务模型的名字,它由三个阶段组成:
- Try 阶段:这个阶段的主要任务是预留资源或执行初步操作,但不提交事务。Try 阶段确保所有相关操作都可以成功执行,且不会出现资源冲突。例如,在预订系统中,这个阶段可能包括检查是否有可用的商品并暂时锁定商品。
- Confirm 阶段:如果 Try 阶段成功,则进入 Confirm 阶段。在这个阶段,系统会提交所有操作,确保事务最终生效。因为 Try 阶段已经确保了资源的可用性和一致性,Confirm 阶段的执行应当是无条件的,即这个阶段不会失败。
- Cancel 阶段:如果 Try 阶段失败,或者在某些情况下需要回滚事务,则进入 Cancel 阶段。在这个阶段,系统会撤销 Try 阶段中的所有预留操作,释放资源。Cancel 阶段确保在事务无法完成时,系统可以回到一致状态。
笔者以一个具体的例子帮助你理解 TCC 事务模型。我们继续沿用 5.3.1 节下单的案例,稍微简化下单的逻辑,去除积分服务(不重要),只保留支付和仓库服务。
图 5-3 TCC 事务模型
首先,用户向商店发送购买某商品的交易请求,金额为 ¥100。请看下面的过程:
Try 阶段:创建事务,生成事务 ID,并记录在事务日志中,进入 Try 阶段。该阶段主要预留业务资源,以及做一些初始化工作:
- 与支付服务通信,确认用户是否有足够的余额。若余额足够,将用户的 100 元设置为冻结状态,并通知进行 Confirm 阶段;如果不可行,通知进入 Cancel 阶段。
- 与仓库服务通信,确认商品的库存是否满足。若库存充足,将仓库中该商品的一条库存设置为冻结状态,并通知进行 Confirm 阶段;如果不可行,通知进入 Cancel 阶段。
Confirm 阶段:如果所有服务反馈业务可行,将事务日志状态更新为 Confirm,进入 Confirm 阶段。
- 支付服务:扣除冻结的 100 元。
- 仓库服务:标记冻结的库存为出库状态,并扣减库存。
Cancel 阶:如果 Try 阶段任何一方反馈失败,将事务日志状态更新为 Cancel,进入 Cancel 阶段:
- 支付服务:释放被冻结的 100 元。
- 仓库服务:释放被冻结的库存。
值得注意的是,按照 TCC 事务模型的规定,Confirm 和 Cancel 阶段只返回成功,不会返回失败。如果 Try 阶段之后,出现网络问题或者服务器宕机,那么事务管理器要不断重试 Confirm 阶段或者 Cancel 阶段,直至完成整个事务流程。
由上述操作过程可见,TCC 事务模型其实有点类似 2PC(两阶段提交)的准备阶段和提交阶段,但 TCC 位于用户代码层面,而不是基础设施层面,这为它的实现带来了较高的灵活性,可以根据需要设计资源锁定的粒度。
不过,感知各个阶段的执行情况以及推进执行下一个阶段需要编写大量的逻辑代码,不仅仅是调用一下 Confirm/Cancel 接口那么简单。通常的情况,我们没必要裸编码实现 TCC 事务模型,而是利用分布式事务中间件(如 Seata、ByteTCC)降低编码工作,提升开发效率。
参见 http://adrianmarriott.net/logosroot/papers/LifeBeyondTxns.pdf ↩︎