Seata 1.5.2 源码学习(事务执行)
关于全局事务的执行,虽然之前的文章中也有所涉及,但不够细致,今天再深入的看一下事务的整个执行过程是怎样的。
1. TransactionManager
io.seata.core.model.TransactionManager是事务管理器,它定义了一个全局事务的相关操作
DefaultTransactionManager是TransactionManager的一个实现类
可以看到,所有操作(开启、提交、回滚、查询状态、上报)都是调用TmNettyRemotingClient#sendSyncRequest()方法向TC发请求
2. GlobalTransaction
DefaultGlobalTransaction实现了GlobalTransaction,它代表一个全局事务
有两件事情需要留意,一是transactionManager是什么? 二是GlobalTransactionRole又是什么?
采用静态内部类的形式来构造单例,还记得DefaultRMHandler和DefaultResourceManager也都是通过静态内部类的形式构造单例
3. TransactionalTemplate
TransactionalTemplate是全局事务执行模板,所有业务逻辑都在其定义的模板方法中执行
io.seata.tm.api.TransactionalTemplate#execute()
现在整个过程清楚了,首先根据事务传播特性来创建一个事务对象,然后开启事务,执行业务逻辑处理,最后提交事务,如果业务执行过程中抛异常,则回滚事务。
现在有一个问题,什么情况下会进入TransactionalTemplate#execute(),或者说什么时候调用该方法?
要回答这个问题,又得从io.seata.spring.annotation.GlobalTransactionScanner说起,这个前面已经说过了,想了解的可以再看看之前那篇 https://www.cnblogs.com/cjsblog/p/16866796.html
从GlobalTransactionScanner说起就太长了,直接快进到GlobalTransactionalInterceptor拦截器吧
当被调用的方法上有@GlobalTransactional注解时,就会被拦截,从而进入GlobalTransactionalInterceptor#invoke(),在invoke()里会调用GlobalTransactionalInterceptor#handleGlobalTransaction(),于是顺利进入TransactionalTemplate#execute()
也就是说,当进入第一个@GlobalTransactional方法时,此时全局事务为空,于是创建一个角色为“GlobalTransactionRole.Launcher”的DefaultGlobalTransaction。当方法内部又调用了另一个@GlobalTransactional方法,于是再创建一个角色为“GlobalTransactionRole.Participant”的DefaultGlobalTransaction。以此类推,后面的都是事务“参与者”。
好了,现在事务已经创建,接下来就可以开启事务并执行业务逻辑处理了
可以看到,只有角色为“GlobalTransactionRole.Launcher”的线程才可以执行事务的开启提交回滚操作,而且这些操作的底层都是调用TransactionManager中的方法,最终是调用TmNettyRemotingClient#sendSyncRequest()方法向TC发送同步请求
最后,看一下什么时候回滚
catch捕获到异常就回滚
以上这些说的都是TM,因为是TM在控制整个全局事务的执行,至于RM本地事务的执行要看io.seata.rm.datasource.ConnectionProxy,这个在之前都讲过了
4. GlobalLockTemplate
GlobalLockTemplate是全局锁模板,是需要全局锁的本地事务的一个执行器模板
那么,在哪里用这个"TX_LOCK"线程变量呢?在BaseTransactionalExecutor#execute()
默认ConnectionContext中isGlobalLockRequire为false
现在就很清晰了,当方法上加了@GlobalLock注解后,进入GlobalLockTemplate#execute(),在当前线程上绑定局部变量TX_LOCK=true。当本地事务提交的时候,上下文(ConnectionContext)中isGlobalLockRequire为true,于是给TC发请求查询锁,如果这些数据没有被任何事务加锁,或者被当前事务加锁,则都算获取到锁了,如果被别的事务加锁了,则算获取锁失败。
总结一下锁互斥,分这么几种情况:
- 两个@GlobalTransactional方法之间,会在注册分支事务的时候检查全局锁,注册成功(获取锁成功)才能提交
- 两个@GlobalLock方法之间,会在事务提交前检查全局锁,获取到锁才能提交
- @GlobalTransactional方法与@GlobalLock方法之间,都是在提交前,一个是分支注册检查锁,一个是直接检查锁
还有一个问题,哪些数据会被加锁呢?这就要从io.seata.rm.datasource.exec.ExecuteTemplate#execute()说起了
长话短说,什么样的数据加锁取决于数据库,以及SQL语句,自行理解一下吧
5. 总结
1、Seata到底是如何实现分布式事务的?
- 首先,每个业务系统都要引入seata的jar包,因此每个业务系统都是一个seata client,于是数据源被seata代理,同时所有方法添加拦截器,对加了@GlobalTransactional的方法进行拦截处理;
- 其次,进入事务方法后,按照模板方法定义,在try...catch...finally中先创建事务并开启,接着执行业务处理,如果抛异常则回滚,如果顺利执行完成,则提交;
- 再次,被调用的远程服务在其本地开启事务并执行,将业务处理和undo_log放在同一个事务中,然后向TC注册分支事务,成功后提交本地事务并向TC报告分支状态
- 最后,业务顺利执行完或抛异常后TM向TC发请求可以提交或回滚全局事务了,TC向所有已注册的分支事务发送提交或回滚请求
总之,数据源代理和全局事务扫描是seata实现分布式事务的基础,而TM做的事情就是控制事务的执行,RM做的事就是处理好本地事务的执行,TC是协调器
2、Seata实现的全局事务,它的事务隔离级别是怎样的?会不会出现脏读、幻读、不可重复读?
先看脏读,在全局事务提交之前,分支事务早已提交,因此,默认情况下,其它的事务是可以读取到当前未提交的全局事务的数据的,故而,默认情况下会发生脏读。
举个例子,假设现在有一个全局事务A还没提交,但是其中的分支事务A1已经提交,A2还在没提交,这个时候另一个全局事务B是可以读取到A1已经提交的数据的,也就是在全局事务B中读到了还未提交的全局事务A的数据,这就是脏读。
那么,如何避免脏读呢?
思路是这样的:首先要让Seata意识到这个SQL语句执行时锁,光知道需要锁还不行,还得让它在执行的时候检查是否获取到锁了。一个SELECT语句需要锁就是将其改写成SELECT ... FOR UPDATE的形式,检查锁的话@GlobalTransactional或@GlobalLock都可以办到。于是,解决版本就有两个:
- SELECT ... FOR UPDATE + @GlobalTransactional
- SELECT ... FOR UPDATE + @GlobalLock
综上所述,分支事务在提交前先进行分支注册获取全局锁,在全局事务提交成功后释放全局锁。此时,其它全局事务可以读取到已提交的分支事务的数据,但这是当前全局事务还未提交,于是出现脏读。办法也很简单,首先select加for update,其次业务方法加@GlobalTransactional或@GlobalLock注解。
同理,默认是可能出现幻读和不可重复读的,它俩属于是脏写,究其原因还是因为跨数据库了,seata搞了个全局锁,这就相当于将业务中几个不同的数据库看成一个数据库,全局锁就相当于这个大数据库中的行级锁,因此解决办法还是一样
不得不说,Seata真的是一个优秀的分布式事务框架
3、AT模式、TCC模式、Saga模式、XA模式的区别
AT模式是基于支持本地事务的关系型数据库
TCC模式不依赖于数据库的事务支持,另外TCC没有全局锁,也就没有锁竞争,故而效率比AT模式高
Saga模式是seata提供的长事务解决方案
XA模式以 XA 协议的机制来管理分支事务的一种事务模式
Seata 1.5.2 源码学习(事务执行)的更多相关文章
- Seata 1.5.2 源码学习
文章有点长,我决定用半个小时来给您分享~ 基于Seata 1.5.2,项目中用 seata-spring-boot-starter 1. SeataDataSourceAutoConfiguratio ...
- ThinkPHP5.0源码学习之执行应用
一.应用启动 在/thinkphp/start.php文件中,用一句代码App::run()->send();实现应用的启动. // 执行应用 App::run()->send(); ...
- Seata Server 1.5.2 源码学习
Seata 包括 Server端和Client端.Seata中有三种角色:TC.TM.RM,其中,Server端就是TC,TM和RM属Client端.Client端的源码学习上一篇已讲过,详见 < ...
- 框架源码系列十一:事务管理(Spring事务管理的特点、事务概念学习、Spring事务使用学习、Spring事务管理API学习、Spring事务源码学习)
一.Spring事务管理的特点 Spring框架为事务管理提供一套统一的抽象,带来的好处有:1. 跨不同事务API的统一的编程模型,无论你使用的是jdbc.jta.jpa.hibernate.2. 支 ...
- Spring5.0源码学习系列之事务管理概述
Spring5.0源码学习系列之事务管理概述(十一),在学习事务管理的源码之前,需要对事务的基本理论比较熟悉,所以本章节会对事务管理的基本理论进行描述 1.什么是事务? 事务就是一组原子性的SQL操作 ...
- Spring 源码学习笔记11——Spring事务
Spring 源码学习笔记11--Spring事务 Spring事务是基于Spring Aop的扩展 AOP的知识参见<Spring 源码学习笔记10--Spring AOP> 图片参考了 ...
- 源码学习之ASP.NET MVC Application Using Entity Framework
源码学习的重要性,再一次让人信服. ASP.NET MVC Application Using Entity Framework Code First 做MVC已经有段时间了,但看了一些CodePle ...
- Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件
写在前面 从大四实习至今已一年有余,作为一个程序员,一直没有用心去记录自己工作中遇到的问题,甚是惭愧,打算从今日起开始养成写博客的习惯.作为一名java开发人员,Spring是永远绕不过的话题,它的设 ...
- mongo源码学习(三)请求接收传输层
在上一篇博客中(mongo源码学习(二)db.cpp之mongoDbMain方法分析),我们把db.cpp中的mongoDbMain的执行过程分析了一下,最后会调用initAndListen(serv ...
- spring源码学习——spring整体架构和设计理念
Spring是在Rod Johnson的<Expert One-On-One J2EE Development and Design >的基础上衍生而来的.主要目的是通过使用基本的java ...
随机推荐
- KingbaseES 支持列加密
KINGBASE 列加密支持 sm4 和 rc4 加密算法,具体算法在 initdb 时指定,默认是 sm4.要使用列加密,必须 shared_preload_libraries = 'sysencr ...
- 当 SQL DELETE 邂逅 Table aliases,会擦出怎样的火花
开心一刻 晚上,女儿眼噙泪水躺在床上 女儿:你口口声声说爱我,说陪我,却天天想着骗我零花钱,你是我亲爹吗? 我:你想知道真相 女儿:想! 我:那你先给爸爸两百块钱! 环境准备 MySQL 不同版本 利 ...
- H5页面调用admob激励视频,用户获取奖励
应用前提条件 使用 Android Studio 3.2 或更高版本 确保您应用的 build 文件使用以下值: minSdkVersion 为 16 或更高版本 compileSdkVersion ...
- jmeter性能测试之正则提取响应头或者响应体
准备工作做好,先发送请求 然后察看结果树中的响应消息 比如我们要提取这个cookie,先调试一下,看能不能提取到 看蓝色的线条,我们提取到了,然后我们把这句话写入到后置处理器中的正则表达式提取里 再次 ...
- 完整的WindowsServer服务器系统初始化配置、安全策略加固和基线检查脚本等保2.0适用
转载自:https://www.bilibili.com/read/cv14326780?spm_id_from=333.999.0.0 0x00 前言简述 最近单位在做等保测评,由于本人从事安全运维 ...
- k8s pod被驱逐问题分析及解决
- Elasticsearch:Elasticsearch中的refresh和flush操作指南
在今天的文章里,我们来主要介绍一下Elasticsearch的refresh及flush两种操作的区别.如果我们从字面的意思上讲,好像都是刷新的意思.但是在Elasticsearch中,这两种操作是有 ...
- centos7安装sonarqube-9.2.4
官方文档地址:https://docs.sonarqube.org/latest/requirements/requirements/ 使用 sonarqube 对 java 项目代码进行扫描的时候, ...
- 论Elasticsearch数据建模的重要性
文章转载自: https://mp.weixin.qq.com/s?__biz=MzI2NDY1MTA3OQ==&mid=2247484159&idx=1&sn=731562a ...
- 企业微信报警中关于markdown的用法
官方文档地址:https://open.work.weixin.qq.com/api/doc/90002/90151/90853#markdown消息 请求方式:POST(HTTPS) 请求地址: h ...