一、Seata 介绍

官网:Seata 官网

Seata 是2019 年阿里巴巴中间件团队发起的开源项目,其前身是 Fescar(Fast & EaSy Commit And Rollback)。其愿景是让分布式的使用像本地事务的使用一样简单、高效,并逐步解决开发者遇到的分布式事务方面的所有难题。

Seata 意为 Simple Extensible Autonomous Transaction Architecture,简单可扩展自治分布式框架。

Seata 的设计目标是对业务无侵入,因此从业务无侵入的 2PC 方案入手,在传统 2PC 的基础上扩展,把一个分布式事务理解成一个包含若干分支事务的全局事务
在这里插入图片描述

二、Seata 的工作原理

2.1 三个角色

1、TC:Transaction Coordinator 事务协调器,管理全局的分支事务状态,用于全局性事务的提交和回滚。
2、TM:Transaction Manager 事务管理器,用于开启、提交或回滚全局事务。
3、RM:Resource Manager 资源管理器,用于分支事务上的资源管理,向 TC 注册分支事务,上报分支事务的状态,接收 TC 的命令来提交或回滚分支事务。
在这里插入图片描述

2.2 工作流程

假设上图中的三个服务根据调用关系为 A–> B–>C,那么 Seata 的执行流程如下:

1、A 服务的TM 向TC申请开启一个全局事务,TC会创建一个全局事务并返回一个 唯一的 XID。
2、A 服务的 RM向 TC注册分支事务,并将其纳入 XID 对应全局事务的管辖。
3、A 服务执行分支事务,操作数据库。
4、A 服务开始远程调用 B 服务,此时XID 会在微服务的调用链上传播。
5、B 服务的 RM 向TC 注册分支事务,并将其纳入 XID 对应的全局事务的管辖。
6、B 服务执行分支事务,操作数据库。
7、全局事务调用链处理完毕,TM 根据有无异常向 TC 发起全局事务的提交或回滚。
8、TC 协调其管辖下的所有分支事务,决定是否回滚。

Seata 实现 2PC 与传统 2PC 的区别:

1、架构层次方面,传统 2PC 方案的 RM 实际上是在数据库层,RM 本质就是数据库,通过 XA 协议实现,而 Seata 的 RM 是以 jar 包的形式作为中间件层部署在应用程序这一侧的。
2、两阶段提交方面,传统 2PC 无论第二阶段的决议是 commit 还是 rollback ,事务性资源的锁都要保持到 Phase 2 完成才释放。而 Seata 的做法是在 Phase 1 就将本地事务提交,这样就可以省去 Phase 2 持锁的时间,整体提高效率。

三、Seata AT 工作机制

Seata 在 2PC 提交模型的基础上进行了演进,视为 Seata AT 模式。除此之外,还有 Seata TCC模式、Seata XA 模式等。

Seata AT 模式非常简单,也是两阶段提交。

  1. 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
  2. 二阶段:- 异步提交,快速完成;- 回滚,通过一阶段回滚日志进行反向补偿

下面以一个简单示例说明整体 AT 模式的工作机制,参考原文:Seata AT 模式

3.1 一阶段

商品表:product :

Field Type Key
id bigint(20) PRI
name varchar(100)
since varchar(100)

业务逻辑:

update product set name = 'GTS' where name = 'TXC';

1、解析SQL:得到 Sql 类型(update),表(product),条件等信息。
2、查询前镜像:根据解析得到的条件,生成查询语句,定位数据:

select id, name, since from product where name = 'TXC';
id name since
1 TXC 2014

3、执行业务 SQL:更新这条记录的 name 为 ‘GTS’。
4、查询后镜像:根据前镜像的结果,通过 主键 定位数据。

select id, name, since from product where id = 1;
id name since
1 GTS 2014

5、插入回滚日志:把前后镜像数据以及业务 SQL 相关的信息组成一条回滚日志记录,插入到 UNDO_LOG 表中。

{
	"branchId": 641789253,
	"undoItems": [{
		"afterImage": {
			"rows": [{
				"fields": [{
					"name": "id",
					"type": 4,
					"value": 1
				}, {
					"name": "name",
					"type": 12,
					"value": "GTS"
				}, {
					"name": "since",
					"type": 12,
					"value": "2014"
				}]
			}],
			"tableName": "product"
		},
		"beforeImage": {
			"rows": [{
				"fields": [{
					"name": "id",
					"type": 4,
					"value": 1
				}, {
					"name": "name",
					"type": 12,
					"value": "TXC"
				}, {
					"name": "since",
					"type": 12,
					"value": "2014"
				}]
			}],
			"tableName": "product"
		},
		"sqlType": "UPDATE"
	}],
	"xid": "xid:xxx"
}

6、提交前,向 TC 注册分支:申请 product 表中,主键值等于 1 的记录的 全局锁 。
7、本地事务提交:业务数据的更新和前面步骤中生成的 UNDO LOG 一并提交。
8、将本地事务提交的结果上报给 TC。

3.2 二阶段

回滚
1、收到 TC 的分支回滚请求,开启一个本地事务,执行如下操作。
2、通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录。
3、数据校验:拿 UNDO LOG 中的后镜与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改。这种情况,需要根据配置策略来做处理,详细的说明在另外的文档中介绍。
4、根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句:

update product set name = 'TXC' where id = 1;

5、提交本地事务。并把本地事务的执行结果(即分支事务回滚的结果)上报给 TC。

提交
1、收到 TC 的分支提交请求,把请求放入一个异步任务的队列中,马上返回提交成功的结果给 TC。
2、异步任务阶段的分支提交请求将异步和批量地删除相应 UNDO LOG 记录。

四、案例演示(待补充)

演示一个电商下订单扣库存的应用案例,看下 Seata 是如何使用的。

Logo

更多推荐