分布式事务解决方案--seata AT模式

demo地址:https://github.com/q464610036/seata
脚本:demo中seata\src\main\resources\sql

1简介

Seata是一款开源的分布式事务解决方案,前身为Fescar,致力于提供高性能和简单易用的分布式事务服务。Seata将为用户提供AT、TCC、SAGA和XA事务模式,为用户打造一站式的分布式解决方案(AT模式是阿里首推模式,阿里云上有商用版本的GTS[Global Transaction service全局事务服务])。
官网地址:https://seata.io/zh-cn/
发展史:
2014 - 阿里中间件团队发布txc(taobao transaction constructor)在阿里内部提供分布式事务服务;
2016 - txc经过改造和升级,变成了gts(global transaction service)在阿里云作为服务对外开放,也成为当时唯一一款对外的服务;
2019 - 阿里经过txc和gts的技术积累,决定开源(Apache开源协议)。并且,在github上发起了一个项目叫做fescar(fast easy commit and rollback)开始拥有了社区群体;
2019 - fescar被重命名为了seata(simple extensiable autonomous transaction architecture),项目迁移到了新的github地址。

2角色划分

RM(ResourceManager资源管理者)理解为我们的一个一个微服务,也叫事务参与者。
TM(TranactionManager事务管理者)也是我们的一个微服务,但该微服务是一个leader,充当全局事务的发起者(决定了全局事务的开启、回滚、提交等),凡是微服务中标注了@GlobalTransactional,那么该微服务就会被看做一个TM。我们的业务场景中订单微服务就是一个事务发起者,同时也是一个RM。
TC(全局事务的协调者)就是我们的seata-server,用来保存全局事务、分支事务、全局锁等自己录,然后会通知各个RM进行回滚或者提交。
分布式事务解决方案--seata AT模式_第1张图片

3安装与集成

3.1seata server(TC)

3.1.1下载
历史版本:https://github.com/seata/seata/releases
官方示例:https://github.com/seata/seata-samples
demo地址:https://github.com/q464610036/seata
建议使用V1.0以及以上,V1.0以下不适合商用,因为seata-server不能提供解决集群模式,不能实现高可用。

3.1.2数据库
1、seata、seata_order、seata_product、seata_account
2、seata库执行sql脚本创建3张表:global_table(全局事务表)、branch_table(分支事务表)、lock_table(全局锁表),脚本在源码script\server\db路径下,我的demo中seata\src\main\resources\sql也有脚本。
3、各业务库执行脚本创建1张表:undo_log(回滚表,保存sql执行前和执行后快照)
3.1.3存储模式
修改配置文件file.conf,配置数据存储模式
分布式事务解决方案--seata AT模式_第2张图片
3.1.4注册中心
修改配置文件registry.conf,配置注册方式
分布式事务解决方案--seata AT模式_第3张图片
3.1.5启动
点击nacos\bin目录下startup.cmd启动即可。

3.2业务系统集成(RM、TM)

3.2.1maven依赖
seata-spring-boot-starter的版本最好与seata-server的版本一致,不然可能会报错(spring-cloud-alibaba-seata 2.2.0.RELEASE自带seata-spring-boot-starter 1.0,但是好像与1.2的seata server不兼容,所以重新引进了seata-spring-boot-starter 1.2)

io.seata seata-spring-boot-starter 1.2.0 com.alibaba.cloud spring-cloud-alibaba-seata 2.2.0.RELEASE

3.2.2yml配置
主要配置seata部分,3个微服务都要配置。
分布式事务解决方案--seata AT模式_第4张图片
3.2.3@GlobalTransactional
只需要在事务管理者service方法配置此注解即可实现全局事务,可认为此注解与@Transactional一样,只不过是全局事务。
分布式事务解决方案--seata AT模式_第5张图片
3.2.4异常
1、seata no available service ‘null’ found, please make sure registry config correct
(1)、seata-spring-boot-starter和server-server的版本是否一致,这里本文都是用1.2
(2)、微服务配置中的seata-registry-nacos-application是否与seata-server中registry.conf配置的nacos-application是否一致,默认为seata-server
分布式事务解决方案--seata AT模式_第6张图片
分布式事务解决方案--seata AT模式_第7张图片

3.3写隔离

1、一阶段本地事务提交前,需要确保先拿到 全局锁 。
2、拿不到全局锁,不能提交本地事务。
3、拿全局锁的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁。
以一个示例来说明:
两个全局事务 tx1 和 tx2,分别对 a 表的 m 字段进行更新操作,m 的初始值 1000。
tx1 先开始,开启本地事务,拿到本地锁,更新操作 m = 1000 - 100 = 900。本地事务提交前,先拿到该记录的 全局锁 ,本地提交释放本地锁。 tx2 后开始,开启本地事务,拿到本地锁,更新操作 m = 900 - 100 = 800。本地事务提交前,尝试拿该记录的 全局锁 ,tx1 全局提交前,该记录的全局锁被 tx1 持有,tx2 需要重试等待 全局锁 。
分布式事务解决方案--seata AT模式_第8张图片
tx1 二阶段全局提交,释放 全局锁 。tx2 拿到 全局锁 提交本地事务。
分布式事务解决方案--seata AT模式_第9张图片
如果 tx1 的二阶段全局回滚,则 tx1 需要重新获取该数据的本地锁,进行反向补偿的更新操作,实现分支的回滚。
此时,如果 tx2 仍在等待该数据的 全局锁,同时持有本地锁,则 tx1 的分支回滚会失败。分支的回滚会一直重试,直到 tx2 的 全局锁 等锁超时,放弃 全局锁 并回滚本地事务释放本地锁,tx1 的分支回滚最终成功。
因为整个过程 全局锁 在 tx1 结束前一直是被 tx1 持有的,所以不会发生 脏写 的问题。

3.4读隔离

在数据库本地事务隔离级别 读已提交(Read Committed) 或以上的基础上,Seata(AT 模式)的默认全局隔离级别是 读未提交(Read Uncommitted) 。
如果应用在特定场景下,必需要求全局的 读已提交 ,目前 Seata 的方式是通过 SELECT FOR UPDATE 语句的代理。
分布式事务解决方案--seata AT模式_第10张图片
SELECT FOR UPDATE 语句的执行会申请 全局锁 ,如果 全局锁 被其他事务持有,则释放本地锁(回滚 SELECT FOR UPDATE 语句的本地执行)并重试。这个过程中,查询是被 block 住的,直到 全局锁 拿到,即读取的相关数据是 已提交 的,才返回。
出于总体性能上的考虑,Seata 目前的方案并没有对所有 SELECT 语句都进行代理,仅针对 FOR UPDATE 的 SELECT 语句。

4支持的模式

4.1AT模式

分布式事务解决方案--seata AT模式_第11张图片
Seata AT模式流程图
1、首先TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID。
(开启全局事务:GlobalTransactionalInterceptor会拦截@GlobalTransactional注解的方法,生成全局事务ID(XID),XID会在整个分布式事务中传递。)。
2、XID 在微服务调用链路的上下文中传播。
(在远程调用时,spring-cloud-alibaba-seata会拦截Feign调用将XID传递到下游服务)
3、RM 开始执行这个分支事务,RM首先解析这条SQL语句,生成对应的UNDO_LOG记录。下面是一条UNDO_LOG中的记录:
分布式事务解决方案--seata AT模式_第12张图片
可以看到,UNDO_LOG表中记录了分支ID,全局事务ID,以及事务执行的redo和undo数据以供二阶段恢复。
(每个RM使用DataSourceProxy连接数据库,其目的是使用ConnectionProxy,使用数据源和数据连接代理的目的就在第一阶段将undo_log和业务数据放在一个本地事务提交,这样就保存了只要有业务操作就一定有undo_log)
4、RM在同一个本地事务中执行业务SQL和UNDO_LOG数据的插入。在提交这个本地事务前,RM会向TC申请关于这条记录的全局锁。如果申请不到,则说明有其他事务也在对这条记录进行操作,因此它会在一段时间内重试,重试失败则回滚本地事务,并向TC汇报本地事务执行失败。
5、RM在事务提交前,申请到了相关记录的全局锁,因此直接提交本地事务,并向TC汇报本地事务执行成功。此时全局锁并没有释放,全局锁的释放取决于二阶段是提交命令还是回滚命令。
6、TC根据所有的分支事务执行结果,向RM下发提交或回滚命令。
7、RM如果收到TC的提交命令,首先立即释放相关记录的全局锁,然后把提交请求放入一个异步任务的队列中,马上返回提交成功的结果给 TC。异步队列中的提交请求真正执行时,只是删除相应 UNDO LOG 记录而已。

RM如果收到TC的回滚命令,则会开启一个本地事务,通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录。将 UNDO LOG 中的后镜与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改。这种情况,需要根据配置策略来做处理。否则,根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句并执行,然后提交本地事务达到回滚的目的,最后释放相关记录的全局锁。

4.2TCC模式(未完成)

4.3SAGA模式(未完成)

4.4XA模式(未完成)

4.5参考

Seata官网:
https://seata.io/zh-cn/
详细部署与搭建:
http://www.iocoder.cn/Seata/install/?self
http://www.iocoder.cn/Spring-Cloud-Alibaba/Seata/

你可能感兴趣的:(分布式与微服务)