分布式事务(五)- TCC

551 阅读4分钟

「这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战

TCC 事务介绍

关于TCC(Try-Confirm-Cancel)的概念,最早是由Pat Helland于2007年发表的一篇名为《Life beyond Distributed Transactions:an Apostate’s Opinion》的论文提出。在该论文中,TCC还是以Tentative-Confirmation-Cancellation命名。正式以Try-Confirm-Cancel作为名称的是Atomikos公司。
TCC和严格遵守ACID的2PC方案不同,主要实现的是最终一致性,且主要面向业务层面的事务控制,通过对业务逻辑(业务系统自己实现)的调度来实现分布式事务,TCC将事务提交分为Try - Confirm - Cancel 3个操作:

  • Try: 完成所有业务检查(一致性),预留业务资源(准隔离性)
  • Confirm:确认执行业务操作,不做任何业务检查, 只使用Try阶段预留的业务资源。
  • Cancel:取消Try阶段预留的业务资源。

dcece60bef035aee4d28d8688cf1bee2.jpeg

案例分析

使用携程App订北京到成都的机票,但是没有直达的,只能从杭州中转,一次购票需要买北京到杭州(东方航空)、杭州到成都(南方航空)两张机票。只买北京到杭州或者杭州到成都的都没什么意义,退票还得收手续费。

携程要保证如果任一航班购买失败,都不能扣钱,可以怎么做呢?

东航、南航都为携程提供以下3个接口:机票预留接口、机票确认接口、机票取消接口。携程分2个阶段进行调用:

  • 第一阶段:携程分别请求两个航空公司预留机票,两个航空公司分别返回预留成功or失败。航空公司需要保证,机票预留成功的话,之后一定能购买到。
  • 第二阶段:两个航空公司都预留成功,发送确认购买请求。如果两个航空公司有一个预留失败,则需要调用预留成功的取消接口取消之前的预留,

备注:实际不确定是怎么实现的,航空公司大概率不会配合你改

TCC存在的问题

幂等

问题:因为网络抖动等原因,分布式事务框架可能会重复调用同一个分布式事务中的一个分支事务的二阶段接口。所以分支事务的二阶段接口Confirm/Cancel需要能够保证幂等性。如果二阶段接口不能保证幂等性,则会产生严重的问题,造成资源的重复使用或者重复释放,进而导致业务故障。

解决:事务状态控制记录作为控制手段,状态可分为初始状态、已确认、已回滚。只有初始状态才允许执行二阶段,其他状态直接返回结果。如何实现幂等可参考:juejin.cn/post/705715…

空回滚

问题:当没有调用参与方Try方法的情况下,就调用了二阶段的Cancel方法,Cancel方法需要有办法识别出此时Try有没有执行。如果Try还没执行,表示这个Cancel操作是无效的,即本次Cancel属于空回滚;如果Try已经执行,那么执行的是正常的回滚逻辑。出现场景:网络超时、服务超时。

解决:同样可以通过状态解决,当Try方法被成功执行后,会插入一条记录,标识该分支事务处于初始状态。当二阶段的Cancel方法被调用时,可以通过查询控制表的对应记录进行判断。如果记录存在且状态为初始态,就表示一阶段已成功执行,可以正常执行回滚操作,释放预留的资源;如果记录不存在则表示一阶段未执行,本次为空回滚,不释放任何资源。

资源悬挂

问题:TC回滚事务调用二阶段完成空回滚后,一阶段执行成功 解决:事务状态控制记录作为控制手段,二阶段发现无记录时插入记录,一阶段执行时检查记录是否存在

优缺点

优点

保证了隔离性,方案灵活,基本能覆盖所有业务场景,可操作性强。

缺点

因为事务状态管理,将产生多次DB操作,这将损耗一定的性能,并使得整个TCC事务时间拉长。 事务涉及方越多,Try、Confirm、Cancel中的代码就越复杂,可复用性就越底(这一点主要是相对最终一致性方案而言的)。

开源框架

最受好评:tcc-Transaction