[分布式事务-TCC] 1. 分布式事务的由来和TCC的核心思想和工作流程
Posted dm_vincent
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[分布式事务-TCC] 1. 分布式事务的由来和TCC的核心思想和工作流程相关的知识,希望对你有一定的参考价值。
文章导航
分布式事务的由来和TCC的核心思想和工作流程
写在前面
分布式事务是一个大的命题,有很多相关的技术和细节。网上介绍这个主题的文章也是多如牛毛,但是能把问题的来龙去脉给讲清楚的文章并不多。
我也不敢说自己就一定能够把这个本来就很复杂的事情讲的有多通透。不过技术教学相长,知识常看常新。做一些努力将这个问题系统性地梳理二三也算是对自己日常的学习和工作做一点沉淀吧。
本系列文章计划是写系列文章来介绍TCC这一分布式事务在应用层的实现方案,包含下面几个主题:
- 分布式事务的由来和TCC的核心思想和工作流程
- TCC的异常处理方案
- TCC的各种优化方案
引子
系统初期一般都是单体应用架构(也即所谓的Monolith型应用),此时系统的所有业务服务都由少数几个系统对外提供。随着业务发展,单体应用会逐渐臃肿成为大杂烩,且系统内部耦合严重,牵一发而动全身。此时一般会针对应用提供的功能进行垂直拆分,实现应用的轻量化和微服务化。
由于原来的应用被拆分为数个微服务,原来的系统内部调用现在就不可避免的成为了分布式环境下的网络调用。因此一个简单的服务调用也会面临分布式环境下的一系列挑战:
- 网络可靠性问题
- 数据一致性问题
- 高可用以及灾备问题
针对每个问题都有各自的解决方案,本文主要聚焦于数据一致性问题。
分布式事务是解决分布式环境下的数据一致性问题的一种规范。而本文要讨论的TCC则是分布式事务的一种实现方案。
TCC是什么
TCC指的是一种资源,它是实现了Try-Confirm-Cancel这三个操作的接口。如果以经典的转账服务作为例子的话,就像下面这样:
比如我们需要将银行A下的某个账户中的钱转账到银行B下的某个账户,就需要依赖银行A的借记TCC资源和银行B的贷记TCC资源。
TCC体现的是一种资源预留的思想,就如同T - Try所展现的那样。首先会尝试预留业务活动需要的资源,如果所有业务参与方都成功地预留了所需资源,那么就意味着这笔业务能够顺利地进行下去,即C - Confirm的含义;反过来,如果有任何一个业务参与者无法预留所需的资源,那么意味着这笔业务无法推进,需要告知所有参与方释放预留的资源,即C - Cancel的含义。
TCC的核心思想
TCC是在应用层定义了类似2PC的标准两阶段接口,业务系统只需要实现对应的接口就可以使用分布式事务功能。
相对传统的XA模式,TCC去除了资源管理器(RM,通常指的是数据库系统)对于分布式事务的依赖。这是一个很大的优势,在如今分布式系统下大规模的部署能够支持分布式事务的资源管理器本身就是不现实的。
业务系统如果想要作为参与者加入到一笔分布式事务中来,只需要提供符合TCC规范的三种原子服务,并保证这些服务的幂等性。
另外,由于分布式事务未达到终态前会存在短暂的数据不一致现象,业务系统也需要能够兼容数据最终一致前带来的可见性问题。
关于短暂的数据不一致,可以举个简单的例子来帮助理解:对于从A账户转账到B账户的场景。
当A已经扣款而B还未收款的中间状态,用户需要知道的是这笔钱正处于转账的途中(预留状态),而不是看到这笔钱莫名其妙地消失了然而B账户还未收到。
当这笔转账的分布式事务走到终态后:
- 成功,A账户和B账户的金额的变更符合用户预期
- 失败,A账户和B账户的金额未发生变更,且会告知用户原因
由于TCC旨在将分布式事务通过划分应用业务逻辑来解决,因此在业务模型的设计上也会体现TCC的思想。以账户模型为例,原本账户金额可能只用一个余额字段表达就够了,现在引入了资源预留的概念后,这个余额字段需要拆分为三个:
- 账户余额
- 处于事务中的入金额 - 可以理解为未达金额
- 处于事务中的出金额 - 可以理解为预留金额
后面会对如何操作这几个字段进行说明。
TCC关联概念
很明显,要完成一笔分布式事务,TCC资源只是一环,还需要几个关键角色的通力合作:
- 分布式事务发起方
- 分布式事务协调者(Transaction Coordinator - 后文中简称为TC)
- 分布式事务元数据存储
分布式事务发起方的主要责任是启动分布式事务,同时负责调用参与者的服务,并记录相应的事务日志。
分布式事务协调者的主要责任是保障分布式事务在各种状况下都能够推进到终态。
分布式事务元数据存储则指的是事务信息的保存地点,事务信息包含下面三类:
- 主事务记录 - Activity
- 分支事务记录 - Action
- 事务状态控制记录 - 主要用于异常处理
TCC调用
TCC资源的调用和其定义是无关的。调用方式则取决于TCC资源的部署视图。总体而言,有三种调用方式:
- 服务内部调用
- 域内服务间调用
- 跨域服务间调用
服务内部调用
如果银行A和银行B正好就是同一家银行,那么这一家银行可以选择将这两个TCC资源部署到一个服务中。此时这两个服务的调用方式就类似于JVM内部调用。
域内服务间调用
如果银行A和银行B正好就是同一家银行,那么这一家银行可以选择将这两个TCC资源部署到不同的服务中。此时这两个服务的调用方式就类似服务间的RPC调用。
跨域服务间调用
如果银行A和银行B不是同一家银行,那么这两个服务势必是在不同的服务中,并且它们的部署都不在一个域内。需要通过各个银行对外暴露的网关接口来完成调用。
一个例子:转账场景下的TCC实现
如果只讲理论的话很容易把自己弄晕,还是以经典的转账为例,来看看如何通过TCC来实现该场景下的分布式事务。
业务场景:银行A的某一账户转账100元给银行B的某一账户。A的当前可用余额为200元。
整体流程表达如下:
先来看看万事顺意的场景。
银行A作为整个分布式事务的发起方,会开启主事务,然后调用银行B的TCC资源一阶段TRY,通知它有一笔未达金额就要来了。
银行B检查目标账户的状态,额度。没有异常的话增加账户的未达金额,通知银行A我已经就绪,就剩二阶段的执行。
银行A得知银行B准备就绪了,开始执行自己的一阶段。自己作为扣款方,需要减少余额并增加未出金额(体现为预留资源)。准备就绪后会通知TC,可以通知各个参与方执行二阶段的Confirm。
二阶段Confirm的操作也很直观了:对于银行A,将事务中的预留金额减去即可;对于银行B,需要将未达金额减去并增加对应的金额到余额。
显然,事情像上面那样顺利显然是不现实的。如果这个过程中有任何异常发生,比如银行B检查目标账户状态异常,会通过抛异常回滚本地事务,返回异常信息告知银行A这笔业务玩不了,银行A收到后直接放弃自己的一阶段执行,这笔分布式事务就此结束。
上面只列出了一种业务异常场景。业务异常场景的分析并不困难,在理解了业务规则的情况下通常都比较直观。根据各个接入分布式事务的业务特点进行分析即可。
在分布式环境下,对于网络异常以及系统异常场景的分析和处理才是老大难。网络交互越复杂,参与者越多那么异常的场景也就越多。
在下篇文章中,会系统性地看看TCC中的异常场景以及相应的应对手段。
以上是关于[分布式事务-TCC] 1. 分布式事务的由来和TCC的核心思想和工作流程的主要内容,如果未能解决你的问题,请参考以下文章
分布式事务——分布式事务简介分布式事务框架 Seata(AT模式Tcc模式Tcc Vs AT)分布式事务—MQ
探秘蚂蚁金服分布式事务 Seata 的ATSaga和TCC模式