Seata
是阿里开源的分布式事务解决方案,本文将详细介绍 Seata 的事务模式、原理以及使用。了解之前需清楚
什么是分布式事务
。
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了XA、AT、TCC 和 SAGA 事务模式,为用户打造一站式的分布式解决方案。
Seata 的几种角色:
角色 | 说明 |
---|---|
TC |
Transaction Coordinator, 事务协调者 ,用来协调全局和各个分支事务(不同服务)的状态, 驱动它们的回滚或提交。 |
TM |
Transaction Manager, 事务管理者 ,业务层中用来开启/提交/回滚一个整体事务(在调用服务的方法中用注解开启事务)。 |
RM |
Resource Manager, 资源管理者 ,管理分支事务,与 TC 进行协调注册分支事务,并且汇报分支事务的状态,驱动分支事务的提交或回滚。 |
简单流程图:
Seata 的 XA 模式大体与 2PC 事务相似。
第一阶段:
第二阶段:
优点:
缺点:
Auto Transaction,基于 XA 演进而来,需要数据库支持,如果是 MySQL,则需要5.6以上版本才支持XA协议。
是一种
无侵入
的分布式事务解决方案,该模式下,用户只需关注自己的业务 SQL,Seata 框架会
在第一阶段拦截并解析 SQL,生成 undo log
,并自动生成事务二阶段的提交和回滚操作。
AT 模式下,是利用快照实现数据回滚,属于弱一致。
第一阶段:
第二阶段:
例如,一个分支业务需要对
account
余额表中的
money
进行扣减 10 元,则需要进行如下流程:
如下图所示,并发事务之间,可能会产生脏写导致数据修改被覆盖。
如何解决脏写,Seata 通过全局锁来管理事务,持有全局锁的事务才有执行 SQL 的权利,这里全局锁
只针对交由 Seata 管理的事务
。
如下图,简单流程大致如下:
那么非 Seata 事务于 Seata 事务并发修改数据时如何处理?
RM 在第一阶段将分支事务注册到 TC 时,会在 undo log 保存两个数据快照,分别是:
before-image
after-image
当发生异常时,
before-image
用来做数据回滚,
after-image
用
来判断修改后数据于当前数据是否相同
,相同则通过
before-image
做数据回滚,不同则说明被其他非 Seata 事务修改过,记录异常,人工介入。
具体流程见下图。
与脏写类似,是指在全局事务未提交前,被其它业务读到已提交的分支事务的数据,
本质上 Seata 默认的全局事务是读未提交
。
那么怎么避免脏读现象呢?
@GlobalTransactional
@GlobalLock
select for update
这样在执行 SQL 前
会检查全局锁是否存在,只有当全局锁完成之后,才能继续执行 SQL
,这样就防止了脏读。
不过,AT 事务模式下读已提交的成本很高,对于非必要场景还是要尽量避免使用。
传统的读已提交不需要本地锁,但这里却需要
select for update
语句,查询多出了加锁和竞争的开销,另外还要持锁调用 TC 的lockQuery接口以判断全局锁情况。
优点:
缺点:
关于什么是 TCC 模式及原理,详情见
什么是分布式事务
。
TCC 与 AT 模式很相似,每阶段都是独立事务,不同的是 TCC 通过人工编码来实现数据恢复。
TCC 每个阶段是做什么的:
Try
Confirm
Try
Confirm
Cancel
Try
TCC 不存在资源阻塞的问题,因为每个方法都直接进行事务的提交,一旦出现异常通过则
Cancel
来进行回滚补偿,这也就是常说的
补偿性事务
举例,
一个扣减用户愈合的业务,假设账户 A 原来的余额是 100,需要扣减 30 元。
什么是空回滚?
分支事务
Try
操作阻塞时,可能导致全局事务超时触发
Cancel
操作。在
Try
未执行时先执行了
Cancel
,这时的
Cancel
理论上不应该回滚,这时就需要
空回滚
。
什么是业务悬挂?
对于已经空回滚的业务,这时如果线程不再阻塞,继续执行
Try
,但不可能
Confirm
或
Cancel
,这就是业务悬挂,需要避免空回滚后的
Try
操作。
如何解决空回滚和业务悬挂?
回滚时需要在执行
Cancel
操作时,判断有没有执行
Try
操作。相应的,在执行
Try
时判断有没有该事务是否回滚过。
这里,我们假设需要在冻结金额的时候进行事务操作。为了实现空回滚,防止业务悬挂,以及幂等性要求。我们必须在数据库记录冻结金额的同时,
记录当前事务 ID 和执行状态
,冻结金额表如下设计:
CREATE TABLE 'account_freeze_tbl'(
'xid' varchar (128) NOT NULL,
'user_id' varchar(255) DEFAULT NULL COMMENT '用户id',
'freeze_money' int(11) unsigned DEFAULT '0' COMMENT '冻结金额',
'state' int(1) DEFAULT NULL COMMENT '事务状态, O:try, 1:confirm, 2:cancel',
PRIMARY KEY ('xid') USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
表字段设计完成后,执行如下的业务逻辑即可避免空回滚和业务悬挂。
优点:
缺点:
Confirm
Cancel
关于什么是 Saga 模式及原理,详情见
什么是分布式事务
。
Saga 模式是 Seata 提供的
长事务解决方案
。也分为两个阶段:
优点:
缺点:
具体代码使用,可参考
Seata 官方文档
。
这里需要注意每个模式需要的准备工作不同,如AT模式下就需要准备如下几点:
对比维度 | XA | AT | TCC | Saga |
---|---|---|---|---|
数据一致性 | 强一致性 | 弱一致性 | 最终一致性 | 最终一致性 |
隔离性 | 完全隔离 | 基于全局锁 | 基于资源预留 | 无隔离 |
代码入侵 | 无 | 无 | 有 | 有 |
性能 | 低 | 较低 | 中 | 高 |
依赖本地事务 | 依赖 | 依赖 | 不依赖 | 不依赖 |
场景 | 一致性,隔离性要求高的业务场景。 | 继续关系型数据库的大多分布式事务的场景均适合。 | 对性能要求高,且有非关系型数据库参与的事务。 | 业务流程较长,数据时效性要求较低的场景。 |
参考:
[1] B站黑马. Seata从入门到进阶.