demo地址:https://github.com/q464610036/seata
脚本:demo中seata\src\main\resources\sql
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地址。
RM(ResourceManager资源管理者)理解为我们的一个一个微服务,也叫事务参与者。
TM(TranactionManager事务管理者)也是我们的一个微服务,但该微服务是一个leader,充当全局事务的发起者(决定了全局事务的开启、回滚、提交等),凡是微服务中标注了@GlobalTransactional,那么该微服务就会被看做一个TM。我们的业务场景中订单微服务就是一个事务发起者,同时也是一个RM。
TC(全局事务的协调者)就是我们的seata-server,用来保存全局事务、分支事务、全局锁等自己录,然后会通知各个RM进行回滚或者提交。
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,配置数据存储模式
3.1.4注册中心
修改配置文件registry.conf,配置注册方式
3.1.5启动
点击nacos\bin目录下startup.cmd启动即可。
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)
3.2.2yml配置
主要配置seata部分,3个微服务都要配置。
3.2.3@GlobalTransactional
只需要在事务管理者service方法配置此注解即可实现全局事务,可认为此注解与@Transactional一样,只不过是全局事务。
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
1、一阶段本地事务提交前,需要确保先拿到 全局锁 。
2、拿不到全局锁,不能提交本地事务。
3、拿全局锁的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁。
以一个示例来说明:
两个全局事务 tx1 和 tx2,分别对 a 表的 m 字段进行更新操作,m 的初始值 1000。
tx1 先开始,开启本地事务,拿到本地锁,更新操作 m = 1000 - 100 = 900。本地事务提交前,先拿到该记录的 全局锁 ,本地提交释放本地锁。 tx2 后开始,开启本地事务,拿到本地锁,更新操作 m = 900 - 100 = 800。本地事务提交前,尝试拿该记录的 全局锁 ,tx1 全局提交前,该记录的全局锁被 tx1 持有,tx2 需要重试等待 全局锁 。
tx1 二阶段全局提交,释放 全局锁 。tx2 拿到 全局锁 提交本地事务。
如果 tx1 的二阶段全局回滚,则 tx1 需要重新获取该数据的本地锁,进行反向补偿的更新操作,实现分支的回滚。
此时,如果 tx2 仍在等待该数据的 全局锁,同时持有本地锁,则 tx1 的分支回滚会失败。分支的回滚会一直重试,直到 tx2 的 全局锁 等锁超时,放弃 全局锁 并回滚本地事务释放本地锁,tx1 的分支回滚最终成功。
因为整个过程 全局锁 在 tx1 结束前一直是被 tx1 持有的,所以不会发生 脏写 的问题。
在数据库本地事务隔离级别 读已提交(Read Committed) 或以上的基础上,Seata(AT 模式)的默认全局隔离级别是 读未提交(Read Uncommitted) 。
如果应用在特定场景下,必需要求全局的 读已提交 ,目前 Seata 的方式是通过 SELECT FOR UPDATE 语句的代理。
SELECT FOR UPDATE 语句的执行会申请 全局锁 ,如果 全局锁 被其他事务持有,则释放本地锁(回滚 SELECT FOR UPDATE 语句的本地执行)并重试。这个过程中,查询是被 block 住的,直到 全局锁 拿到,即读取的相关数据是 已提交 的,才返回。
出于总体性能上的考虑,Seata 目前的方案并没有对所有 SELECT 语句都进行代理,仅针对 FOR UPDATE 的 SELECT 语句。
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中的记录:
可以看到,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 的相关信息生成并执行回滚的语句并执行,然后提交本地事务达到回滚的目的,最后释放相关记录的全局锁。
Seata官网:
https://seata.io/zh-cn/
详细部署与搭建:
http://www.iocoder.cn/Seata/install/?self
http://www.iocoder.cn/Spring-Cloud-Alibaba/Seata/