共计 4344 个字符,预计需要花费 11 分钟才能阅读完成。
一、背景
随着业务倒退,单体零碎逐步无奈满足业务的需要,分布式架构逐步成为大型互联网平台首选。随同而来的问题是,本地事务计划曾经无奈满足,分布式事务相干标准和框架应运而生。
在这种状况下,大型厂商依据分布式事务实现标准,实现了不同的分布式框架,以简化业务开发者解决分布式事务相干工作,让开发者专一于外围业务开发。
Seata 就是这么一个分布式事务处理框架,Seata 是由阿里开源,前身为 Fescar,通过品牌降级变身 Seata。
二、分布式事务标准
1. 分布式事务相干概念
事务: 一个程序执行单元,是用户定义的一组操作序列,须要满足 ACID 属性。
本地事务: 事务由本地资源管理器治理。
分布式事务: 事务的操作位于不同的节点。
分支事务: 在分布式事务中,由资源管理器治理的本地事务。
全局事务: 一次性操作多个资源管理器实现的事务,由一组分支事务组成。
2. 分布式事务实现标准
对于本地事务,能够借助 DBMS 零碎来实现事务的治理,然而对于分布式事务,它就无能为力了。对于分布式事务,目前次要有 2 种思路:XA 协定的强统一标准以及柔性事务的最终一致性标准。
2.1 XA
XA 是基于 2 阶段提交协定设计的接口标准,实现了 XA 标准的资源管理器就能够参加 XA 全局事务。利用承当事务管理器 TM 工作,数据库承当资源管理器 RM 工作,TM 生成全局事务 id,管制 RM 的提交和回滚。
2.2 柔性事务的最终一致性
该标准次要有 3 种实现形式,TCC、MQ 事务音讯、本地音讯表。(还存在其余一些不罕用实现形式如 Saga)。
TCC:try/confirm/cancel,在 try 阶段锁定资源,confirm 阶段进行提交,资源锁定失败执行 cancel 阶段开释资源。
MQ 事务音讯: 前提音讯零碎须要反对事务如 RocketMQ,在本地事务执行前,发送事务音讯 prepare,本地事务执行胜利,发送事务音讯 commit,实现分布式事务最终一致性。如果事务音讯 commit 失败,RocketMQ 会回查音讯发送者确保音讯失常提交,如果步骤 5 执行失败,进行重试,达到最终一致性。
本地音讯表: 跟 MQ 事务音讯相似,区别在于 MQ 不反对事务音讯,须要借助本地数据库的事务管理能力。在步骤 1 中将须要发送的音讯和本地事务一起提交到 DB,借助 DB 的事务管理确保音讯长久化。步骤 2 利用通过本地音讯表扫描,重试发送,确保音讯能够发送胜利。
三、Seata 架构
1. 零碎组成
Seata 有三个外围组件:
- Transaction Coordinator(TC,事务协调器)
保护全局事务和分支事务的状态,驱动全局事务提交或回滚。
- Transaction Manager(TM,事务管理器)
定义全局事务的范畴,开始事务、提交事务、回滚事务。
- Resource Manager(RM,资源管理器):
治理分支事务上的资源,向 TC 注册分支事务,汇报分支事务状态,驱动分支事务的提交或回滚。
三个组件相互协作,TC 以 Server 模式独立部署,TM 和 RM 集成在利用中启动,其整体交互如下:
2. 工作模式
Seata 反对四种工作模式:
2.1 AT(Auto Transaction)
AT 模式是 Seata 默认的工作模式。须要基于反对本地 ACID 事务的关系型数据库,Java 利用,通过 JDBC 拜访数据库。
2.1.1 整体机制
该模式是 XA 协定的演变,XA 协定是基于资源管理器实现,而 AT 并不是如此。AT 的 2 个阶段别离是:
- 一阶段: 业务数据和回滚日志记录在同一个本地事务中提交,开释本地锁和连贯资源。
- 二阶段: 提交异步化,十分疾速地实现;回滚通过一阶段的回滚日志进行反向弥补。
下图中,步骤 1 开启全局事务;步骤 2 注册分支事务,这里对应着一阶段;步骤 3 提交或者回滚分支事务,对应着二阶段。
2.1.2 特点
- 长处: 对代码无侵入;并发度高,本地锁在一阶段就会开释;不须要数据库对 XA 协定的反对。
- 毛病: 只能用在反对 ACID 的关系型数据库;SQL 解析还不能反对全副语法。
2.2 TCC
该模式工作分为三个阶段:prepare/commit/cancel。
2.2.1 整体机制
- TM 向 TC 申请全局事务 XID,流传给各个子调用。
- 子调用的所在 TM 向 TC 注册分支事务,并执行本地 prepare,并向 TC 报告执行后果。
- TC 依据各分支事务的执行后果确定二阶段是执行 commit 或 rollback。
2.2.2 特点
- 长处: 不依赖本地事务。
- 毛病: 回滚逻辑依赖手动编码;业务侵入性较大。
2.3 Saga 模式
2.3.1 Saga 是什么?
1987 年普林斯顿大学的 Hector Garcia-Molina 和 Kenneth Salem 发表了一篇 Paper Sagas,讲述的是如何解决 long lived transaction(长活事务)。Saga 是一个长活事务可被分解成能够交织运行的子事务汇合。论文见这里。
简略来说,Saga 将一个长事务(T)分解成一系列 Sub 事务(Ti),每个 Sub 事务都有对应的弥补动作(Ci),用于撤销 Ti 事务产生的影响。Sub 事务是间接提交到库,在出现异常时,逆向进行弥补。
因而 Saga 事务的组成有 2 种:
- T1, T2, T3, …, Tn
- T1, T2, …, Tj, Cj,…, C2, C1,其中 0 < j < n
第一种就是失常提交的状况,第二种在提交 Tj 事务出现异常,开始逆向弥补的状况。
Saga 模式是 Seata 提供的长事务解决方案。例如全局事务中波及到内部零碎,无奈治理它的资源管理器,让它革新成 TCC 也不好履行,这时就能够采纳此类计划。
2.3.2 整体机制
在 Saga 模式中,业务流程中每个参与者都提交本地事务,当呈现某一个参与者失败则弥补后面曾经胜利的参与者,一阶段正向服务和二阶段弥补服务都由业务开发实现。
上图中对于多个分支事务,省略了屡次呈现的 2.* 步骤。对于全局事务执行过程中业务利用宕机状况,业务利用集群中对等节点会通过从 TC 获取相干会话,从 DB 加载详细信息来复原状态机。
2.3.3 特点
- 长处: 一阶段提交本地事务,无锁,高性能;事件驱动架构,参与者可异步执行,高吞吐;弥补服务易于实现。
- 毛病: 不保障隔离性。
2.4 XA 模式
XA 是基于二阶段提交设计的接口标准。对于反对 XA 的资源管理器,借助 Seata 框架的 XA 模式,会使 XA 计划更简略易用。应用前提:须要分支数据库反对 XA 事务,利用为 Java 利用,且应用 JDBC 拜访数据库。
2.4.1 整体机制
在 Seata 定义的分布式事务框架内,利用事务资源(数据库、音讯服务等)对 XA 协定的反对,以 XA 协定的机制来治理分支事务的一种 事务模式。
- 执行阶段: 业务 sql 在 XA 分支中执行,由分支事务的 RM 管理器治理,而后执行 XA prepare。
- 实现阶段:TM 依据各个分支执行后果通过 TC 告诉各个分支执行 XA commit 或者 XA rollback。
2.4.2 特点
- 长处: 继承了 XA 协定的劣势,事务具备强一致性。
- 毛病: 同样继承了 XA 协定的劣势,因为分支事务长时间开启,并发度低。
2.5 Seata 各模式比照
分布式事务计划没有银弹,依据本人的业务个性抉择适合的模式。例如谋求强一致性,能够抉择 AT 和 XA,存在和内部零碎对接,能够抉择 Saga 模式,不能依赖本地事务,能够采纳 TCC 等等。联合各模式的优缺点进行抉择。
四、AT 模式外围实现
鉴于 Seata 反对的模式较多,而其默认的模式是 AT,为节俭篇幅,以下围绕 AT 模式分析其相干的外围模块实现。
1. 事务协调器的启动
TC(事务协调器)以独立的服务启动,作为 Server,保护全局事务和分支事务的状态,驱动全局事务提交或回滚。上面是 TC 的启动流程:
2. 事务管理器的启动
TM(事务管理器)集成在利用中启动,负责定义全局事务的范畴,开始事务、提交事务、回滚事务。
TM 所在利用中须要配置 GlobalTransactionScannerbean,在利用启动时会进行如下初始化流程:
3. 资源管理器的启动
RM(资源管理器)集成在利用中启动,负责管理分支事务上的资源,向 TC 注册分支事务,汇报分支事务状态,驱动分支事务的提交或回滚。
RM 所在的利用中除了须要跟 TM 一样配置 GlobalTransactionScanner 以启动 RMClient,还须要配置 DataSourceProxy,以实现对数据源拜访代理。该数据源代理实现了 sql 的解析 → 生成 undo-log → 业务 sql 和 undo-log 一并本地提交等操作。
4. 全局事务的工作流程
上面以一个简略的例子来阐明全局事务的工作原理:
- BusinessService: 发动购买服务
- StorageService: 库存治理服务
购买操作实现在 businessService.purchase 中,purchase 办法实现上通过 GlobalTransaction 注解,通过 Dubbo 服务,调用了库存服务 deduct 办法办法,样例如下:
@GlobalTransactional(timeoutMills = 300000, name = "dubbo-demo-tx")
public void purchase(String userId, String commodityCode, int orderCount) {storageService.deduct(commodityCode, orderCount);
// throw new RuntimeException("xxx");
}
4.1 胜利的全局事务处理流程
4.2 胜利的全局事务处理流程
这里设定 BusinessService 在胜利调用 StorageService 后,本地出现异常。
5. 写隔离实现
全局事务未提交,分支事务本地曾经提交的状况下(假如批改了资源 A),如何防止其余事务在此时批改资源 A?Seata 采纳全局锁来实现,其流程如下:
6. 读隔离实现
在数据库本地隔离级别为读已提交或以上的根底上,Seata 提供了读未提交,这个很好了解,全局事务提交前分支事务本地曾经提交。如果想要实现读已提交,则须要在 select 语句上加 for update。
五、总结
Seata 是 Java 畛域很弱小的分布式事务框架,其反对了多种模式。其中默认反对的 AT 模式,相比于传统的 2PC 协定(基于数据库的 XA 协定),很好地解决了 2PC 长期锁资源的问题,进步了并发度。Seata 反对的各个模式中,AT 模式对业务零入侵实现分布式事务,对于开发者更加敌对。另外 Seata 的 Server 在抉择适合的存储介质时能够进行集群模式,缩小单点故障影响。
本文次要参考官网和局部博客,同时浏览了 AT 模式实现源码,如果有不对的中央,望指出,一起探讨交换。
六、参考
- Seata 的 Saga 模式
- 依据 GlobalTransactional 注解生成代理类
- Seata 我的项目地址
- Seata 应用
作者:vivo 官网商城开发团队