【理论指导实践】Seata-TCC模式

2,651 阅读7分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第 33 天,点击查看活动详情

我是石页兄,朋友不因远而疏,高山不隔友谊情;偶遇美羊羊,我们互相鼓励

欢迎关注微信公众号「架构染色」交流和学习

image.png


一、背景

伴随着高性能的分布式系统演进,我们必然会经历 通过横向扩展节点 提高非热点数据的并发性能;而横向扩展节点实际是如下2方面的扩展变化:

  1. 扩展功能节点(对应应用的微服务化改造)
  2. 扩展数据节点(对应增加数据分片)

这些变化自然就引发 原来调用一个服务的一个接口就完成的功能,现在需要协同调用多个服务的多个接口才能完成。相信我们都遇到过因网络、机器、程序等不可靠,引发的数据一致性的问题。而数据的一致性与系统的可扩展性和高可用同样重要 是【基础IT架构】支撑业务高质量转型升级的重点也是难点。从蚂蚁金服的分布事务产品十几年的Roadmap可以看出其在数据一致性方面的坚持和不易(如今能快速使用他们的积累,幸甚至哉!),如下图所示:

image.png

从官宣得知,蚂蚁金服从微服务演进开始至今,大量的应用采用了TCC事务模型,可能也是得益于:

  1. TCC模式关注功能扩展,在按照功能横向扩展资源时,解决微服务间调用的一致性问题,保证多资源访问的事务属性。

  2. 在早期没有现成成熟的分布式框架的条件下,其事务模型运转在业务层,不依赖底层数据资源的事务支持,能灵活应对服务的拆分。

  3. 也可能跟2007年TCC概念的提出有重大关联

二、起源

TCC(Try-Confirm-Cancel)的概念,最早是由 Pat Helland 于 2007 年发表的一篇名为《Life beyond Distributed Transactions:an Apostate’s Opinion》的论文提出。

三、核心思想

其核心思想是是:通过对资源进行预留,尽量减少对资源的锁定时间;如果事务提交则完成对预留资源的确认;如果事务回滚,则释放预留的资源。

四、角色和职责

4.1、服务实现3个方法

根据自己的业务场景服务实现3个自定义的方法,把服务纳入到全局事务的管理中,3个方法概述如下:

  1. Try 方法:尝试执行;完成所有业务检查(一致性), 预留必须业务资源(准隔离性)。

  2. Confirm 方法:确认执行真正执行业务,不作任何业务检查,只使用 Try 阶段预留的业务资源,只要try成功,那么confirm就必须成功;Confirm 操作要求具备幂等设计,Confirm 失败后需要进行重试。

  3. Cancel 方法:取消执行,释放 Try 阶段预留的业务资源。Cancel 阶段的异常和 Confirm 阶段异常处理方案基本上一致,要求满足幂等设计。

4.2、角色

Seata的实现中有3个重要角色

image.png

1)TC (Transaction Coordinator) - 事务协调者

维护全局和分支事务的状态,驱动全局事务提交或回滚。

2)TM (Transaction Manager) - 事务管理器(发起方)

定义全局事务的范围:开始全局事务、提交或回滚全局事务。

3)RM (Resource Manager) - 资源管理器(参与者) 提供TCC服务,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

4.3、二阶段的职责分工

image.png

  1. 第一阶段: 要求服务提供方RM必须接受一些不确定因素,在1阶段所提供的逻辑操作是临时性的操作(try操作),调用方TM保留了后续取消这些操作的权利。

  2. 第二阶段:

    • 如果调用方决策认为整个事务应该回滚,会要求取消之前的临时性操作,对应的是提供方的的Cancel操作。
    • 如果调用方决策认为整个事务应该提交,会放弃取消的权利,对应的是提供方的Confirm操作。

如转账作为例子,通常会在Try里面冻结金额,但不扣款,Confirm里面扣款,Cancel里面解冻金额:

image.png

五、工作原理

5.1、核心流程

Seata 框架管控整个事务,业务只需要主动调 Try 方法;TM 负责2件事情:1.发起全局事务,2.提交或回滚全局事务到 TC,然后TC 负责调用Confirm或Cancel的事(包括重试);核心流程如下:

image.png

(来自网络)

  1. TM发起全局事务
  2. TM调用分支事务RM的try方法
  3. RM在Try 执行前注册分支事务到 TC
  4. RM在Try 执行完后,上报事务状态给 TC
  5. TM通知TC进行全局事务的提交或回滚
  6. TC调度,执行分支事务RM的 Confirm 或 Cancel

5.2、注意事项

1) 空回滚

  • 场景:Try 未执行,Cancel 执行了
  • 原因:Try超时(丢包)了
  • 解决:当 TC 发现少了一个 Try,那它就会回滚整个分布式事务,于是触发了 Cancel,直接 Cancel 可能出现数据一致性问题,所以就要记录 Try 是否执行,没执行就要做空回滚(啥都不做直接返回成功)。

2) 防悬挂

  • 场景:Cancel 比 Try 先执行
  • 原因:Try超时(阻塞)了
  • 解决:当 TC 发现少了一个 Try,那它就会回滚整个分布式事务,于是触发了 Cancel,然后拥堵的 Try 在 Cancel 处理完(空回滚)又来了,这时同样要记录下整个状态,要拒绝空回滚以后的 Try

3) 幂等控制

  • 现象:超时重试、补偿都会导致 TCC 服务的 Try、Confirm 或 Cancel 操作被重复执行
  • 解决:Confirm 和 Cancel 开发时要考虑幂等控制(这是必须要做的)

六、总结

概括来看,该模式有以下几个特点:

  1. 该模式对代码的嵌入性高,开发工作较多,要求每个业务需要写TCC三步骤的操作。

  2. 无论有没有本地事务控制都可以使用该模式,与底层数据库事务实现不相关,这个事务模式是抽象的基于服务层的概念,无论DB层是否有JDBC支持,即使没有JDBC的支持如redis、mongo、es等,只要将对他们的操作包装成TCC的参与者,就可以接入到TCC的事务范围内。

  3. 并发度高,无需长期资源锁定,但并发问题、数据的一致性问题,需由开发者自行根据业务场景控制,开发要求高、难度偏大。

  4. 适用于订单类业务,可以有中间状态,但对中间状态有约束的业务。

  5. 提供了更多的灵活性,因为是业务自主定义实现,用户可以借助2阶段的提交过程,容易实现在特定场景下的自定义优化和特殊功能开发。这个模式几乎能满足任何想要的事务场景,自定义补偿性,自定义资源预留型事务,消息事务等场景。


参考并感谢

七、最后说一句

我是石页兄,如果这篇文章对您有帮助,或者有所启发的话,欢迎关注笔者的微信公众号【 架构染色 】进行交流和学习。您的支持是我坚持写作最大的动力。

欢迎点击链接扫马儿关注、交流。