一、背景
伴随着高性能的分布式系统演进,我们必然会经历 通过横向扩展节点 提高非热点数据的并发性能;而横向扩展节点实际是如下2方面的扩展变化:
- 扩展功能节点(对应应用的微服务化改造)
- 扩展数据节点(对应增加数据分片)
这些变化自然就引发 原来调用一个服务的一个接口就完成的功能,现在需要协同调用多个服务的多个接口才能完成。相信我们都遇到过因网络、机器、程序等不可靠,引发的数据一致性的问题。而数据的一致性与系统的可扩展性和高可用同样重要 是【基础IT架构】支撑业务高质量转型升级的重点也是难点。从蚂蚁金服的分布事务产品十几年的Roadmap可以看出其在数据一致性方面的坚持和不易(如今能快速使用他们的积累,幸甚至哉!),如下图所示:
从官宣得知,蚂蚁金服从微服务演进开始至今,大量的应用采用了TCC事务模型,可能也是得益于:
- TCC模式关注功能扩展,在按照功能横向扩展资源时,解决微服务间调用的一致性问题,保证多资源访问的事务属性。
- 在早期没有现成成熟的分布式框架的条件下,其事务模型运转在业务层,不依赖底层数据资源的事务支持,能灵活应对服务的拆分。
- 也可能跟2007年TCC概念的提出有重大关联
二、起源
TCC(Try-Confirm-Cancel)的概念,最早是由 Pat Helland 于 2007 年发表的一篇名为《Life beyond Distributed Transactions:an Apostate’s Opinion》的论文提出。
三、核心思想
其核心思想是是:通过对资源进行预留,尽量减少对资源的锁定时间;如果事务提交则完成对预留资源的确认;如果事务回滚,则释放预留的资源。
四、角色和职责
4.1、服务实现3个方法
根据自己的业务场景服务实现3个自定义的方法,把服务纳入到全局事务的管理中,3个方法概述如下:
- Try 方法:尝试执行;完成所有业务检查(一致性), 预留必须业务资源(准隔离性)。
- Confirm 方法:确认执行真正执行业务,不作任何业务检查,只使用 Try 阶段预留的业务资源,只要try成功,那么confirm就必须成功;Confirm 操作要求具备幂等设计,Confirm 失败后需要进行重试。
- Cancel 方法:取消执行,释放 Try 阶段预留的业务资源。Cancel 阶段的异常和 Confirm 阶段异常处理方案基本上一致,要求满足幂等设计。
4.2、角色
Seata的实现中有3个重要角色
1)TC (Transaction Coordinator) - 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。
2)TM (Transaction Manager) - 事务管理器(发起方)
定义全局事务的范围:开始全局事务、提交或回滚全局事务。
3)RM (Resource Manager) - 资源管理器(参与者) 提供TCC服务,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
4.3、二阶段的职责分工
- 第一阶段: 要求服务提供方RM必须接受一些不确定因素,在1阶段所提供的逻辑操作是临时性的操作(try操作),调用方TM保留了后续取消这些操作的权利。
- 第二阶段:
- 如果调用方决策认为整个事务应该回滚,会要求取消之前的临时性操作,对应的是提供方的的Cancel操作。
- 如果调用方决策认为整个事务应该提交,会放弃取消的权利,对应的是提供方的Confirm操作。
如转账作为例子,通常会在Try里面冻结金额,但不扣款,Confirm里面扣款,Cancel里面解冻金额:
五、工作原理
5.1、核心流程
Seata 框架管控整个事务,业务只需要主动调 Try 方法;TM 负责2件事情:1.发起全局事务,2.提交或回滚全局事务到 TC,然后TC 负责调用Confirm或Cancel的事(包括重试);核心流程如下:
(来自网络)
- TM发起全局事务
- TM调用分支事务RM的try方法
- RM在Try 执行前注册分支事务到 TC
- RM在Try 执行完后,上报事务状态给 TC
- TM通知TC进行全局事务的提交或回滚
- 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 开发时要考虑幂等控制(这是必须要做的)
六、总结
概括来看,该模式有以下几个特点:
- 该模式对代码的嵌入性高,开发工作较多,要求每个业务需要写TCC三步骤的操作。
- 无论有没有本地事务控制都可以使用该模式,与底层数据库事务实现不相关,这个事务模式是抽象的基于服务层的概念,无论DB层是否有JDBC支持,即使没有JDBC的支持如redis、mongo、es等,只要将对他们的操作包装成TCC的参与者,就可以接入到TCC的事务范围内。
- 并发度高,无需长期资源锁定,但并发问题、数据的一致性问题,需由开发者自行根据业务场景控制,开发要求高、难度偏大。
- 适用于订单类业务,可以有中间状态,但对中间状态有约束的业务。
- 提供了更多的灵活性,因为是业务自主定义实现,用户可以借助2阶段的提交过程,容易实现在特定场景下的自定义优化和特殊功能开发。这个模式几乎能满足任何想要的事务场景,自定义补偿性,自定义资源预留型事务,消息事务等场景。
参考并感谢
七、最后说一句
我是石页兄,如果这篇文章对您有帮助,或者有所启发的话,欢迎关注笔者的微信公众号【 架构染色 】进行交流和学习。您的支持是我坚持写作最大的动力。