seata
官方文档: https://seata.io/zh-cn/docs/overview/what-is-seata.html
仓库地址: https://github.com/seata/seata
示例地址: https://github.com/seata/seata-samples
seata
?
什么是seata
是一种高性能、易用的微服务架构分布式事务解决方案。Seata
将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
微服务中的分布式事务问题
传统的单体应用程序使用单一的本地数据源,本地事务会保证数据的一致性。
微服务架构发生了变化,每个服务内的数据一致性自然由本地事务保证,但整个业务逻辑范围涉及到多个数据源则无法保证数据的一致性
seata
是怎么做的
seata
只是上面提到的问题的一种解决方案
首先如何定义 分布式事务?
我们说,分布式事务是由一批 分支事务 组成的 全局事务,通常 分支事务只是 本地事务
seata
框架中的三个角色
TC(Transaction Coordinator)事务协调器:维护全局和分支事务的状态,驱动全局提交或回滚。
TM(Transaction Manager)事务管理器:定义全局事务的范围:开始一个全局事务,提交或回滚一个全局事务。
RM(Resource Manager)资源管理器:管理分支事务处理的资源,与TC通信注册分支事务和报告分支事务状态,并驱动分支事务提交或回滚
seata
托管分布式事务的典型生命周期
- TM 要求 TC 开始新的全局事务。TC 生成代表全局事务的 XID
- XID 通过微服务的调用链传播
- RM 将本地事务作为 XID对应的全局事务的一个分支注册到TC
- TM 请求 TC 提交或回滚 XID 对应的全局事务
- TC 驱动 XID对应全局事务下的所有分支事务完成分支提交或回滚
AT
模式
前提
- 基于支持本地 ACID 事务的关系型数据库
- Java 应用,通过 JDBC 访问数据库
整体机制
两阶段提交协议的演变
- 一阶段: 业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源
- 二阶段:
- 提交异步化,非常快速的完成
- 回滚通过一阶段的回滚日志进行反向补偿
写隔离
- 一阶段本地事务提交前,需确保先拿到 全局锁
- 拿不到 全局锁,不能提交本地事务
- 拿 全局锁 的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁
读隔离
在数据库本地事务隔离级别 读已提交(Read Committed) 或以上的基础上,Seata
(AT模式)的默认全局隔离级别是 读未提交(Read Uncommitted)
如果应用在特定场景下,必须要求全局 **读已提交,**默认Seata
的方式是通过 SELECT FOR UPDATE
语句的代理。
SELECT FOR UPDATE
语句的执行会申请 全局锁,如果 全局锁 被其他事务持有,则释放本地锁(回滚 SELECT FOR UPDATE
语句的本地执行)并重试。如果这个过程中,查询是被 block 住的,知道 全局锁 拿到,即读取的相关数据是 已提交的,才返回
出于总体性能上的考虑,Seata
目前的方案并没有对所有 SELECT 语句都进行代理,仅针对 FOR UPDATE 的 SELECT 语句。
TCC
模式
一个分布式的全局事务,整体是 两阶段提交的模型。全局事务是由若干分支事务组成的,分支事务要满足 两阶段提交的模型要求,即需要每个分支事务都具备自己的:
- 一阶段 prepare 行为
- 二阶段 commit 或 rollback行为
根据两阶段行为模式的不同,我们将分支事务划分为 Automatic (Branch) Transaction Mode
和 TCC (Branch) Transaction Mode
AT 模式基于 支持本地ACID事务 的关系型数据库:
- 一阶段 prepare 行为:在本地事务中,一并提交业务数据更新和相应回滚日志记录
- 二阶段 commit 行为:马上成功结束, 自动 异步批量清理回滚日志
- 二阶段 rollback 行为:通过回滚日志,自动 生成补偿操作,完成数据回滚
相应的TCC
模式,不依赖于底层数据资源的事务支持:
- 一阶段 prepare 行为:调用 自定义 的 prepare 逻辑。
- 二阶段 commit 行为:调用 自定义 的 commit 逻辑。
- 二阶段 rollback 行为:调用 自定义 的 rollback 逻辑。
所谓TCC
模式,是指支持把 自定义 的分支事务纳入到全局事务的管理中。
Saga
模式
概述
Saga模式是Seata
提供的长事务解决方案,在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。
适用场景:
- 业务流程长、业务流程多
- 参与者包括其它公司或遗留系统服务,无法提供
TCC
模式要求的三个接口
优势
- 一阶段提交本地事务,无锁,高性能
- 事件驱动架构,参与者可异步执行,高吞吐
- 补偿服务易于实现
缺点
- 不保证隔离性
Saga的实现
目前SEATA提供的Saga模式是基于状态机引擎来实现的,机制是:
通过状态图来定义服务调用的流程并生成 json 状态语言定义文件
状态图中一个节点可以是调用一个服务,节点可以配置它的补偿节点
状态图 json 由状态机引擎驱动执行,当出现异常时状态引擎反向执行已成功节点对应的补偿节点将事务回滚
注意: 异常发生时是否进行补偿也可由用户自定义决定
可以实现服务编排需求,支持单项选择、并发、子流程、参数转换、参数映射、服务执行状态判断、异常捕获等功能
XA
模式
前提
- 支持XA事务的数据库
- Java 应用,通过 JDBC 访问数据库。
整体机制
在 Seata 定义的分布式事务框架内,利用事务资源(数据库,消息服务等)对XA协议的支持,以 XA协议的机制来管理分支事务的一种事务模式
执行阶段
- 可回滚:业务 SQL 操作放在 XA 分支中进行,由资源对 XA 协议的支持来保证 可回滚
- 持久化:XA 分支完成后,执行 XA prepare,同样,由资源对 XA 协议的支持来保证 持久化(即,之后任何意外都不会造成无法回滚的情况)
完成阶段
- 分支提交:执行 XA 分支的 commit
- 分支回滚:执行 XA 分支的 rollback
工作机制
整体运行机制
XA 模式 运行在 Seata 定义的事务框架内:
执行阶段
- XA start/XA end/XA prepare + SQL + 注册分支
完成阶段
- XA commit/XA rollback
数据源代理XA 模式需要 XAConnection。
获取 XAConnection 两种方式:
- 方式一:要求开发者配置 XADataSource
- 方式二:根据开发者的普通 DataSource 来创建
第一种方式,给开发者增加了认知负担,需要为 XA 模式专门去学习和使用 XA 数据源,与 透明化 XA 编程模型的设计目标相违背。
第二种方式,对开发者比较友好,和 AT 模式使用一样,开发者完全不必关心 XA 层面的任何问题,保持本地编程模型即可。
我们优先设计实现第二种方式:数据源代理根据普通数据源中获取的普通 JDBC 连接创建出相应的 XAConnection。
分支注册
XA start 需要 Xid 参数。
这个 Xid 需要和 Seata 全局事务的 XID 和 BranchId 关联起来,以便由 TC 驱动 XA 分支的提交或回滚。
目前 Seata 的 BranchId 是在分支注册过程,由 TC 统一生成的,所以 XA 模式分支注册的时机需要在 XA start 之前。
将来一个可能的优化方向:
把分支注册尽量延后。类似 AT 模式在本地事务提交之前才注册分支,避免分支执行失败情况下,没有意义的分支注册。
这个优化方向需要 BranchId 生成机制的变化来配合。BranchId 不通过分支注册过程生成,而是生成后再带着 BranchId 去注册分支。