Seata原理浅析
前言
Seata是阿里开源的分布式事务解决方案,本文将详细介绍 Seata 的事务模式、原理以及使用。了解之前需清楚什么是分布式事务。
一、什么是 Seata
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了XA、AT、TCC 和 SAGA 事务模式,为用户打造一站式的分布式解决方案。
Seata 的几种角色:
| 角色 | 说明 |
|---|---|
| TC | Transaction Coordinator,事务协调者,用来协调全局和各个分支事务(不同服务)的状态, 驱动它们的回滚或提交。 |
| TM | Transaction Manager,事务管理者,业务层中用来开启/提交/回滚一个整体事务(在调用服务的方法中用注解开启事务)。 |
| RM | Resource Manager,资源管理者,管理分支事务,与 TC 进行协调注册分支事务,并且汇报分支事务的状态,驱动分支事务的提交或回滚。 |
简单流程图:

二、事务模式
1. XA 模式
Seata 的 XA 模式大体与 2PC 事务相似。
1.1 流程介绍

第一阶段:
- RM 注册分支事务到 TC;
- RM 执行分支业务的 SQL 但不提交;
- RM 报告执行状态到 TC;
第二阶段:
- TC 检测检测各分支事务状态,判断整体事务提交或回滚;
- RM 接受 TC 的指令,进行统一的提交或回滚操作。
1.2 XA 优缺点
优点:
- 事务强一致性,满足 ACID 原则;
- 实现简单,无代码入侵。
缺点:
- 一阶段锁定资源,二阶段结束才释放,性能较差;
- 依赖关系型数据库实现事务;
2. AT 模式
Auto Transaction,基于 XA 演进而来,需要数据库支持,如果是 MySQL,则需要5.6以上版本才支持XA协议。
是一种无侵入的分布式事务解决方案,该模式下,用户只需关注自己的业务 SQL,Seata 框架会在第一阶段拦截并解析 SQL,生成 undo log,并自动生成事务二阶段的提交和回滚操作。
AT 模式下,是利用快照实现数据回滚,属于弱一致。
2.1 流程介绍

第一阶段:
- RM 注册分支事务到 TC;
- 记录 undo log(数据快照);
- RM 执行分支业务的 SQL 并提交;
- RM 报告执行状态到 TC;
第二阶段:
- TC 检测检测各分支事务状态,判断整体事务提交或回滚;
- RM 接受 TC 的指令,进行统一的提交或回滚操作。
- 提交时,异步删除相应分支的 undo log;
- 回滚时,根据 undo log 生成补偿回滚的 SQL,执行分支回滚并返回结果给 TC;
例如,一个分支业务需要对account余额表中的money进行扣减 10 元,则需要进行如下流程:

2.2 脏写问题
如下图所示,并发事务之间,可能会产生脏写导致数据修改被覆盖。

如何解决脏写,Seata 通过全局锁来管理事务,持有全局锁的事务才有执行 SQL 的权利,这里全局锁只针对交由 Seata 管理的事务。
如下图,简单流程大致如下:
- 一阶段本地事务提交前,需要确保先拿到全局锁 ;
- 拿不到全局锁 ,不能提交本地事务。
- 拿不到全局锁会重试,次数有限,超出限制将放弃,并回滚本地事务,释放本地锁。

2.3 数据快照
那么非 Seata 事务于 Seata 事务并发修改数据时如何处理?
RM 在第一阶段将分支事务注册到 TC 时,会在 undo log 保存两个数据快照,分别是:
before-image:数据修改前的快照after-image:数据修改后的快照
当发生异常时,before-image用来做数据回滚,after-image用来判断修改后数据于当前数据是否相同,相同则通过before-image做数据回滚,不同则说明被其他非 Seata 事务修改过,记录异常,人工介入。
具体流程见下图。

2.4 脏读问题
与脏写类似,是指在全局事务未提交前,被其它业务读到已提交的分支事务的数据,本质上 Seata 默认的全局事务是读未提交。
那么怎么避免脏读现象呢?
- 业务查询时要使用
@GlobalTransactional或@GlobalLock来修饰查询方法的调用; - 查询语句须使用
select for update语句。
这样在执行 SQL 前会检查全局锁是否存在,只有当全局锁完成之后,才能继续执行 SQL,这样就防止了脏读。
不过,AT 事务模式下读已提交的成本很高,对于非必要场景还是要尽量避免使用。
传统的读已提交不需要本地锁,但这里却需要select for update语句,查询多出了加锁和竞争的开销,另外还要持锁调用 TC 的lockQuery接口以判断全局锁情况。
2.5 AT 优缺点
优点:
- 一阶段直接完成事务提交,释放数据库资源,性能比较好;
- 利用全局锁实现读写隔离;
- 没有代码入侵,框架自动完成回滚或提交。
缺点:
- 两阶段之间属于软状态,属于最终一直;
- 数据快照会影响性能,但比 XA 模式要好很多;
3. TCC 模式
关于什么是 TCC 模式及原理,详情见什么是分布式事务。
TCC 与 AT 模式很相似,每阶段都是独立事务,不同的是 TCC 通过人工编码来实现数据恢复。
3.1 流程介绍

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;
表字段设计完成后,执行如下的业务逻辑即可避免空回滚和业务悬挂。

3.2 TCC 优缺点
优点:
- 一阶段直接完成事务提交,释放数据库资源,性能比较好;
- 相比 AT,无需生成快照和使用全局锁,性能最好;
- 不依赖数据库事务,依赖补偿操作,可用于非事务型数据库。
缺点:
- 代码入侵,每个阶段都需要编写对应的业务代码;
- 软状态,属于最终一致;
- 需要考虑
Confirm和Cancel的失败情况,做好幂等处理。
4. Saga 模式
关于什么是 Saga 模式及原理,详情见什么是分布式事务。
Saga 模式是 Seata 提供的长事务解决方案。也分为两个阶段:
- 一阶段: 直接提交本地事务
- 二阶段: 成功则什么都不做;失败则通过编写补偿业务来回滚

优点:
- 事务参与者可以基于事件驱动实现异步调用,吞吐高;
- 一阶段直接提交本地事务,无锁,性能好;
- 代码入侵较 TCC 低,实现简单。
缺点:
- 软状态持续时间不确定,时效性差;
- 没有锁和事务隔离,可能会有脏写。
三、代码实现
具体代码使用,可参考 Seata 官方文档。
这里需要注意每个模式需要的准备工作不同,如AT模式下就需要准备如下几点:
- lock_table 导入 Seata 数据库,就是 TC 服务关联的数据库;
- undo_log 导入业务相关的数据库;
- 修改事务模式。
四、对比总结
| 对比维度 | XA | AT | TCC | Saga |
|---|---|---|---|---|
| 数据一致性 | 强一致性 | 弱一致性 | 最终一致性 | 最终一致性 |
| 隔离性 | 完全隔离 | 基于全局锁 | 基于资源预留 | 无隔离 |
| 代码入侵 | 无 | 无 | 有 | 有 |
| 性能 | 低 | 较低 | 中 | 高 |
| 依赖本地事务 | 依赖 | 依赖 | 不依赖 | 不依赖 |
| 场景 | 一致性,隔离性要求高的业务场景。 | 继续关系型数据库的大多分布式事务的场景均适合。 | 对性能要求高,且有非关系型数据库参与的事务。 | 业务流程较长,数据时效性要求较低的场景。 |
参考:
Seata原理浅析的更多相关文章
- HTTP长连接和短连接原理浅析
原文出自:HTTP长连接和短连接原理浅析
- Javascript自执行匿名函数(function() { })()的原理浅析
匿名函数就是没有函数名的函数.这篇文章主要介绍了Javascript自执行匿名函数(function() { })()的原理浅析的相关资料,需要的朋友可以参考下 函数是JavaScript中最灵活的一 ...
- [转帖]Git数据存储的原理浅析
Git数据存储的原理浅析 https://segmentfault.com/a/1190000016320008 写作背景 进来在闲暇的时间里在看一些关系P2P网络的拓扑发现的内容,重点关注了Ma ...
- Android-Binder原理浅析
Android-Binder原理浅析 学习自 <Android开发艺术探索> 写在前头 在上一章,我们简单的了解了一下Binder并且通过 AIDL完成了一个IPC的DEMO.你可能会好奇 ...
- Dubbo学习(一) Dubbo原理浅析
一.初入Dubbo Dubbo学习文档: http://dubbo.incubator.apache.org/books/dubbo-user-book/ http://dubbo.incubator ...
- 沉淀,再出发:docker的原理浅析
沉淀,再出发:docker的原理浅析 一.前言 在我们使用docker的时候,很多情况下我们对于一些概念的理解是停留在名称和用法的地步,如果更进一步理解了docker的本质,我们的技术一定会有质的进步 ...
- 阻塞和唤醒线程——LockSupport功能简介及原理浅析
目录 1.LockSupport功能简介 1.1 使用wait,notify阻塞唤醒线程 1.2 使用LockSupport阻塞唤醒线程 2. LockSupport的其他特色 2.1 可以先唤醒线程 ...
- 【Spark Core】TaskScheduler源代码与任务提交原理浅析2
引言 上一节<TaskScheduler源代码与任务提交原理浅析1>介绍了TaskScheduler的创建过程,在这一节中,我将承接<Stage生成和Stage源代码浅析>中的 ...
- vue的双向绑定原理浅析与简单实现
很久之前看过vue的一些原理,对其中的双向绑定原理也有一定程度上的了解,只是最近才在项目上使用vue,这才决定好好了解下vue的实现原理,因此这里对vue的双向绑定原理进行浅析,并做一个简单的实现. ...
- 消息队列——ActiveMQ使用及原理浅析
文章目录 引言 正文 一.ActiveMQ是如何产生的? 产生背景 JMS规范 基本概念 JMS体系结构 二.如何使用? 基本功能 消息传递 P2P pub/sub 持久订阅 消息传递的可靠性 事务型 ...
随机推荐
- C++设计模式 - 工厂方法(Factory Method)
对象创建模式 通过"对象创建"模式绕开new ,来避免对象创建( new )过程中所导致的紧耦合(依赖具体类) , 从而支持对象创建的稳定.它是接口抽象之后的第一步工作. 典型模式 ...
- 前端使用 Konva 实现可视化设计器(1)
使用 konva 实现一个设计器交互,首先考虑实现设计器的画布. 一个基本的画布: [展示]网格.比例尺 [交互]拖拽.缩放 "拖拽"是无尽的,"缩放"是基于鼠 ...
- #2-sat,Tarjan#洛谷 4171 [JSOI2010]满汉全席
题目 分析 考虑两个至少选一个就是非A即B,非B即A, 都可行当且仅当A与非A不在同一个强连通分量里 代码 #include <cstdio> #include <cctype> ...
- AtCoder Beginner Contest 181
ABC181 A - Heavy Rotation 题目传送门 代码(签到题) #include <cstdio> #define rr register using namespace ...
- java深入理解浅拷贝和深拷贝
目录 简介 拷贝接口 使用clone导致的浅拷贝 使用clone的深拷贝 不要overridden clone 总结 简介 拷贝对象是java中经常会遇到的问题.java中存在两种类型,基础类型和引用 ...
- MySQL 数据库查询与数据操作:使用 ORDER BY 排序和 DELETE 删除记录
使用 ORDER BY 进行排序 使用 ORDER BY 语句按升序或降序对结果进行排序. ORDER BY 关键字默认按升序排序.要按降序排序结果,使用 DESC 关键字. 示例按名称按字母顺序排序 ...
- 喜报|HarmonyOS开发者社区连获业内奖项,持续深耕开发者生态
临近年末,各大平台陆续揭晓年度榜单,表彰了具备强大影响力与做出突出贡献的优秀项目与团队,而HarmonyOS开发者社区作为技术分享,学习和展示的平台,输出高质量技术文章百余篇,连续获得业内各大奖项, ...
- MySQL集群入门(PXC)
目标: 1.掌握PXC集群MySQL方案的原理: 2.掌握PXC集群的强一致性: 3.掌握PXC集群的高可用方案:硬件要求: 1.Win10x64企业版/linux/MacOS: 2.Docker虚拟 ...
- C++ 类方法解析:内外定义、参数、访问控制与静态方法详解
C++ 类方法 类方法,也称为成员函数,是属于类的函数.它们用于操作或查询类数据,并封装在类定义中.类方法可以分为两种类型: 类内定义方法: 直接在类定义内部声明和定义方法. 类外定义方法: 在类定义 ...
- 树莓派和esp8266之间使用tcp协议通信
树莓派代码: from flask import Flask, render_template import socket import threading app = Flask(__name__) ...