大纲
1.Seata分布式事务框架简介
2.Seata AT模式实现分布式事务的机制
3.Seata AT模式下的写隔离机制
4.Seata AT模式下的读隔离机制
5.官网示例说明Seata AT模式的工作机制
6.Seata TCC模式的介绍以及与AT模式区别
7.Seata Saga模式的介绍
8.单服务多个库的分布式事务支持
9.Seata的AT、TCC、Saga三种模式对比
10.基于RocketMQ的可靠消息最终一致性事务
11.Seata官方分布式事务例子
1.Seata分布式事务框架简介
(1)Seata的特色功能
(2)Seata的术语
Seata是一款分布式事务解决方案,为用户提供了AT、TCC、SAGA和XA事务模式,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。
(1)Seata的特色功能
一.微服务框架集成支持
已支持Dubbo、Spring Cloud、Sofa-RPC、Motan和gRPC等RPC框架。
二.AT模式
提供无侵入自动补偿的事务模式,目前已支持MySQL、Oracle、PostgreSQL、TiDB和MariaDB。
三.TCC模式
支持TCC模式并可与AT混用,灵活度更高。
四.SAGA模式
为长事务提供的解决方案,提供编排式与注解式。
五.XA模式
支持已实现XA接口的数据库的XA模式,目前已支持MySQL、Oracle、TiDB和MariaDB。
六.高可用
支持计算分离集群模式,水平扩展能力强的数据库和Redis存储模式。
(2)Seata的术语
一.TC(Transaction Coordinator)—事务协调者
用来维护全局和分支事务的状态,驱动全局事务提交或回滚。
二.TM(Transaction Manager)—事务管理器
用来定义全局事务的范围:开始全局事务、提交或回滚全局事务。
三.RM(Resource Manager)—资源管理器
用来管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
2.Seata AT模式实现分布式事务的机制
(1)前提
(2)整体机制
(1)前提
一.基于支持本地ACID事务的关系型数据库
二.Java应用 + 通过JDBC访问数据库
(2)整体机制
两阶段提交协议的演变:
一阶段:首先业务数据和回滚日志记录在同一个本地事务中提交,然后释放本地锁和连接资源。
二阶段:提交异步化,非常快速地完成,回滚通过一阶段的回滚日志进行反向补偿。
3.Seata AT模式下的写隔离机制
(1)Seata在AT模式下的写隔离机制
(2)Seata在AT模式下的写隔离例子
(3)Seata AT模式写隔离下的补偿执行
(4)为什么Seata AT模式要设计写隔离机制
(1)Seata在AT模式下的写隔离机制
一阶段本地事务提交前,需要确保先拿到全局锁。如果拿不到全局锁,就不能提交本地事务。拿全局锁的尝试被限制在一定时间范围内,超出时间范围将被放弃,并回滚本地事务,释放本地锁。
(2)Seata在AT模式下的写隔离例子
以一个示例来说明:两个全局事务tx1和tx2,分别对m字段进行更新操作,m的初始值是1000。
tx1先开始,开启本地事务,拿到本地锁,更新操作m=1000-100=900。本地事务提交前,先拿到该记录的全局锁 ,本地事务提交后释放本地锁。
tx2后开始,开启本地事务,拿到本地锁,更新操作m=900-100=800。本地事务提交前,尝试拿该记录的全局锁。
tx1全局提交前,该记录的全局锁被tx1持有,tx2需要重试等待获取全局锁。tx1二阶段全局提交,释放全局锁。tx2拿到全局锁后,提交其本地事务。
(3)Seata AT模式写隔离下的补偿执行
如果tx1的二阶段全局回滚,则tx1需要重新获取该数据的本地锁,进行反向补偿的更新操作,实现分支的回滚。
此时如果tx2仍在等待该数据的全局锁,同时持有本地锁,则tx1的分支回滚会失败,tx1的分支回滚会一直重试,直到tx2的全局锁等锁超时,需要放弃全局锁 + 回滚本地事务 + 释放本地锁,此时tx1的分支回滚才能最终成功。
(4)为什么Seata AT模式要设计写隔离机制
由于整个过程全局锁在tx1结束前一直是被tx1持有的,所以写隔离机制可以保证不会发生脏写的问题。
4.Seata AT模式下的读隔离机制
(1)Seata AT模式的读隔离机制介绍
(2)Seata AT模式的读隔离机制实现
(1)Seata AT模式的读隔离机制介绍
在数据库本地事务隔离级别读已提交(Read Committed)或以上的基础上,Seata AT模式的默认全局隔离级别是读未提交(Read Uncommitted)。
如果应用在特定场景下,必需要求全局的读已提交,目前Seata的方式是通过SELECT FOR UPDATE语句的代理来实现。
(2)Seata AT模式的读隔离机制实现
SELECT FOR UPDATE语句的执行会申请全局锁。如果全局锁被其他事务持有,则释放本地锁(回滚SELECT FOR UPDATE语句的本地执行)并重试。
在这个过程中,查询是被阻塞住的。直到拿到全局锁,即读取的相关数据是已提交的,才返回。
出于总体性能上的考虑,Seata目前的方案并没有对所有SELECT语句都进行代理,仅针对FOR UPDATE的SELECT语句。
5.官网示例说明Seata AT模式的工作机制
(1)一阶段的工作过程
(2)二阶段——回滚的工作过程
(3)二阶段——提交的工作过程
(4)使用Seata AT模式需要创建回滚日志表
以一个示例来说明整个AT分支的工作过程:
业务表:product
+-------+--------------+-------+
| Field | Type | Key |
+-------+--------------+-------+
| id | bigint(20) | PRI |
+-------+--------------+-------+
| name | varchar(100) | |
+-------+--------------+-------+
| since | varchar(100) | |
+-------+--------------+-------+
AT分支事务的业务逻辑:
update product set name = 'GTS' where name = 'TXC';
(1)一阶段的工作过程
步骤一:解析SQL得到SQL的类型、表、条件等相关的信息。比如类型是UPDATE、表是product、条件是where name = 'TXC'。
步骤二:查询前镜像。也就是根据解析得到的条件信息,生成查询语句,定位数据。
select id, name, since from product where name = 'TXC';//得到前镜像如下:
+-------+-------+-------+
| id | name | since |
+-------+-------+-------+
| 1 | TXC | 2014 |
+-------+-------+-------+
步骤三:执行业务SQL。更新这条记录的name为'GTS'。
步骤四:查询后镜像。根据前镜像的结果,通过主键定位数据。
select id, name, since from product where id = 1;//得到后镜像如下:
+-------+-------+-------+
| id | name | since |
+-------+-------+-------+
| 1 | GTS | 2014 |
+-------+-------+-------+
步骤五:插入回滚日志。把前后镜像数据以及业务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"
}
步骤六:提交前向TC注册分支。也就是申请product表中,主键值等于1的记录的全局锁。
步骤七:本地事务提交。业务数据的更新和前面步骤中生成的UNDO LOG一并提交。
步骤八:将本地事务提交的结果上报给TC
(2)二阶段——回滚的工作过程
步骤一:收到TC的分支回滚请求,开启一个本地事务执行如下操作。
步骤二:通过XID和Branch ID查找到相应的UNDO LOG记录。
步骤三:拿UNDO LOG中的后镜像与当前数据进行数据校验。如果有不同,说明数据被当前全局事务之外的动作做了修改。这种情况,需要根据配置策略来做处理。
步骤四:根据UNDO LOG中的前镜像和业务SQL信息生成并执行回滚语句。
步骤五:提交本地事务并把执行结果(即分支事务回滚的结果)上报给TC。
(3)二阶段——提交的工作过程
步骤一:收到TC的分支提交请求,把请求放入一个异步任务的队列中,然后马上返回提交成功的结果给TC。
步骤二:异步任务阶段执行分支提交请求,将异步和批量地删除相应UNDO LOG记录。
(4)使用Seata AT模式需要创建回滚日志表
CREATE TABLE `undo_log` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`branch_id` bigint(20) NOT NULL,`xid` varchar(100) NOT NULL,`context` varchar(128) NOT NULL,`rollback_info` longblob NOT NULL,`log_status` int(11) NOT NULL,`log_created` datetime NOT NULL,`log_modified` datetime NOT NULL,PRIMARY KEY (`id`),UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
6.Seata TCC模式的介绍以及与AT模式区别
(1)Seata TCC模式的介绍
(2)TCC模式与AT模式的区别
(1)Seata TCC模式的介绍
TCC = Try + Commit + Cancel。一个分布式的全局事务,整体是两阶段提交的模型。全局事务是由若干分支事务组成,分支事务要满足两阶段提交的模型要求。
即需要每个分支事务都具备自己的:一阶段Prepare行为(Try行为),二阶段Commit或Rollback行为(Commit + Cancel行为)。
(2)TCC模式与AT模式的区别
所谓TCC模式,是指支持把自定义的分支事务纳入到全局事务的管理中。其实一开始,Seata只有TCC模式,后来才加入AT模式。但TCC模式太麻烦了,每个功能都需要实现三套SQL逻辑,AT模式其实是对TCC模式的简化。
AT模式基于支持本地ACID事务的关系型数据库:
一阶段Prepare行为:在本地事务中,一起提交业务数据更新和相应回滚日志记录。
二阶段Commit行为:马上成功结束,自动 + 异步 + 批量清理回滚日志。
二阶段Rollback行为:通过回滚日志,自动生成补偿操作,完成数据回滚。
TCC模式则不依赖于底层数据资源的事务支持:
一阶段Prepare行为:调用自定义Prepare逻辑。
二阶段Commit行为:调用自定义Commit逻辑。
二阶段Rollback行为:调用自定义Rollback逻辑。
7.Seata Saga模式的介绍
Saga模式是Seata提供的长事务解决方案。在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者。一阶段正向服务和二阶段补偿服务都由业务开发实现。
Saga模式的正向服务可以理解成是TCC模式的Try阶段 + Commit阶段,逆向补偿服务可以理解成是TCC模式的Cancel阶段。
一.适用场景
场景一:业务流程长、业务流程多
场景二:无法提供TCC模式要求的三个接口
比如参与者包含其它公司,或者遗留的老系统服务。
二.优势
一阶段提交本地事务,无锁 + 高性能。
事件驱动架构,参与者可异步执行,高吞吐。
补偿服务易于实现。
三.缺点
不保证隔离性,写不隔离。
8.单服务多个库的分布式事务支持
单个服务对接了多个库。比如该服务处理一个请求时,需要更新多个数据库。分支事务是分布在多个数据库上的,并不是分布在多个服务上的。Seata也天然支持这种情况下的分布式事务。
每个数据库上的事务也都会到Seata上注册一个分支事务。注册之后,各个分支事务也和Seata的AT模式一样:申请本地锁 + 生成undo log + 申请全局锁 + 在其对应数据库上提交增删改。
9.Seata的AT、TCC、Saga三种模式对比
AT模式适用于底层存储都相同的情况,比如都是MySQL等。一般选用AT模式即可,简单方便。
TCC模式适用于底层存储是异构的情况,比如MySQL + Redis + ES等。TCC模式一般在try阶段写入一条数据并标记未生效状态,然后在commit阶段才正式标记为正常生效状态。
TCC模式最大的优点就是很灵活。即使事务失败了,try阶段写入的数据基本也不会影响线上业务。TCC模式特别适用于异构存储要支持事务的情况。
Saga模式适用于异构系统、长流程、改造成TCC特别复杂繁琐的情况。
10.基于RocketMQ的可靠消息最终一致性事务
同步事务:在Seata的AT、TCC、Saga中选一种。
异步事务:使用RocketMQ的事务机制或者自己实现一套最终一致性事务框架。
11.Seata官方分布式事务例子
https://seata.io/zh-cn/docs/user/quickstart.html