首页 > 其他分享 >Seata AT模式学习

Seata AT模式学习

时间:2024-05-02 13:55:05浏览次数:26  
标签:回滚 Seata 本地 事务 模式 update 学习 提交 全局

官方文档

Seata是目前国内最流行的一个分布式事务的组件,支持以下4种模式

AT模式:对业务代码无侵入,只要在业务的数据库加上一个UNDO_LOG表,在配置文件中配置好Seata的服务端,在需要开启全局事务的地方加上注解就行

TCC模式:即Try-Commit-Cancel,自定义prepare逻辑、commit逻辑及回滚的逻辑,代码侵入性大、灵活、对开发要求高

SAGA模式:主要用于分布式长事务

XA模式:即XA协议的实现,经典的二阶段提交

 

这里我主要学习一下最常用的AT模式

 

大致工作流程:

由两阶段提交协议演化而来,也是分为两个阶段,如下

一阶段:解析update SQL和执行业务的UPDATE语句,将回滚的补偿放入undo_log,直接提交本地事务

二阶段(成功):清理undo_log相关的补偿信息

二阶段(失败):根据undo_log种的补偿信息对数据进行反向补偿

 

三种角色:

TC(Transaction Manager):事务协调者,维护全局和分支事务的状态,驱动全局事务提交或回滚。

TM(Transaction Manager): 事务管理器,定义全局事务的范围,开始全局事务、提交或回滚全局事务。相当于加了@GlobalTransaction注解那个才是TM。

RM(Resource Manager ) :资源管理器,管理分支事务处理的资源( Resource ),与 TC 交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。被TM调用的服务会有RM。

其中,TC工作在Seata服务端,TM和RM工作在Seata的客户端即业务的微服务端

 

 

 

 

使用方式:

1. 部署Seata服务端,单机用于学习,生产上一般是要以集群的形式,并配置一个数据库(保存全局的事务id),并将自身注册到服务注册中心,被其他微服务使用

2. 业务端改造,

    在每个相关的业务端的数据库中加上一个undo_log的表,

    

使用方式:只需要将注解@GlobalTransactional加在需要开启全局事务的那个方法上,而被调用的微服务的方法上只需要加上本地事务注解 @Transactional

public class BusinessServiceImpl implements BusinessService {

    private StorageService storageService;

    private OrderService orderService;

@GlobalTransactional public void purchase(String userId, String commodityCode, int orderCount) { storageService.deduct(commodityCode, orderCount); orderService.create(userId, commodityCode, orderCount); } }

 

@GlobalTransactional这个直接会标识开启全局事务,这个应该就是TM,开启全局事务、提交全局事务和回滚全局事务

那为什么被调用的微服务的方法不需要加全局事务的注解呢?

    我大胆猜测一下,因为被调用的微服务方法是具体的干活的,是RM,会将DataSource进行一层代理,在执行UPDATE SQL的前后会进行解析UPDATE SQL和生成补偿信息到undo_log中

那RM怎么知道某个方法被全局事务的进行管理了呢?

    我再来猜测一下,微服务间的调用肯定也做了手脚,会在HTTP调用或者RPC调用的时候将全局事务的事务xid作为参数或者header传过来      

 

关于数据源的代理做了什么事参考官方文档

关于如何传递全局事务id,需要使用改造过的feign/rpc

 

两个主要的注解:

@GlobalTransactional   声明全局事务(会隐式的获取、持有、释放全局锁)

@GlobalLock                 检查是否有全局锁(配合select for update 能进行多次尝试获取全局锁,否则监测到有全局锁就抛出异常)

 

以下是一阶段和二阶段的详细过程

一阶段

  1. 解析 SQL:得到 SQL 的类型(UPDATE),表(product),条件(where name = 'TXC')等相关的信息。
  2. 查询前镜像:根据解析得到的条件信息,生成查询语句,定位数据。
  3. 执行业务 SQL:更新这条记录的 name 为 'GTS'。
  4. 查询后镜像:根据前镜像的结果,通过 主键 定位数据。
  5. 插入回滚日志:把前后镜像数据以及业务 SQL 相关的信息组成一条回滚日志记录,插入到 UNDO_LOG 表中。
  6. 提交前,向 TC 注册分支:申请 product 表中,主键值等于 1 的记录的 全局锁 。
  7. 本地事务提交:业务数据的更新和前面步骤中生成的 UNDO LOG 一并提交。
  8. 将本地事务提交的结果上报给 TC。

二阶段-提交

  1. 收到 TC 的分支提交请求,把请求放入一个异步任务的队列中,马上返回提交成功的结果给 TC。
  2. 异步任务阶段的分支提交请求将异步和批量地删除相应 UNDO LOG 记录。

二阶段-回滚

  1. 收到 TC 的分支回滚请求,开启一个本地事务,执行如下操作。
  2. 通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录。
  3. 数据校验:拿 UNDO LOG 中的后镜与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改。这种情况,需要根据配置策略来做处理,详细的说明在另外的文档中介绍。
  4. 根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句:
  5. 提交本地事务。并把本地事务的执行结果(即分支事务回滚的结果)上报给 TC。

 

 

 

 

关于脏读问题

Seata中的分布式事务,都有各自的 XID,每个 XID 都会把 “行锁”(也叫全局锁)注册到 TC 里面

 

问题原因:由于AT模式是一阶段就直接提交,所以如果另一个不相关的方法去查询对应的那行数据,是有可能读到脏数据(即还未完成全局事务的数据)

解决办法:使用select for update和@GlobalLock注解,如下图,

select for update会等待全局事务中的一阶段结束后才能拿到本地锁,然后去获取全局锁,此时全局锁被全局事务给占用了(TC处有记录),导致全局锁获取不到,从而无法继续下去进行读取操作,直到全局事务二阶段提交或者回滚

 

 

关于脏写问题

问题原因:同样的,由于AT模式是一阶段就直接提交,所以如果另一个不相关的方法(只带了@Transactional,甚至不带)去修改对应的那行数据,是可以在一阶段结束后,二阶段提交回滚前将数据改掉,导致如果全局事务失败,无法正确回滚(补偿)

解决办法一:在其他的update请求的方法上也加上@GlobalTransactional,同样开启全局事务,确保修改的时候能够因为全局锁而被挡住

 

解决办法二:在其他的update请求的方法上加上@GlobalLock+@Transactional,并在update前使用selectForUpdate(这步可以不做,下面解释)。   会先去尝试拿本地锁(直到拿到),然后做修改,再去获取全局锁,此时另一个全局事务还未提交,则会霸占着全局锁,这里取不到全局锁,会释放本地锁,然后抛出获取全局锁失败的异常。

 

这里不使用select for update也能防止脏写,但是加了能带来以下的好处:

  • 锁冲突更“温柔”些。如果只有@GlobalLock,检查到全局锁,则立刻抛出异常,也许再“坚持”那么一下,全局锁就释放了,抛出异常岂不可惜了。
  • updateA()中可以通过select for update获得最新的A,接着再做更新。

 

关于脏读脏写的原因以及解决办法

 

 

写隔离以及是否会死锁

不会死锁,虽然是可能形成两个全局事务相互持有锁(比如一个事务持有本地锁并尝试获取全局锁,一个事务持有全局锁并尝试获取本地锁),不过拿全局锁是会进行有限次数的尝试,拿不到的话会放弃并释放另一个锁,所以并不会形成真正的死锁

如下图官网的例子:

两个全局事务tx1和tx2,分别对a表的一个字段m进行更新,

tx1先开始 -》 开启本地事务 -》 拿到本地锁 -》 更新操作 -》 拿到全局锁 -》 提交并释放本地锁 -》 等待二阶段的提交或者回滚

tx2后开始 -》 开启本地事务 -》 拿到本地锁(如果在tx1释放本地锁前尝试拿的话会等待)-》 更新操作 -》 尝试多次去拿全局锁(全局锁被tx1持有)

如果tx1最后执行的是二阶段提交,则tx1释放全局锁,tx2获取到全局锁,tx2也能完成一阶段的工作,并继续往下执行

 

 

 

如果tx1最后执行的是二阶段回滚,(tx1仍然持有全局锁)tx1还需要重新获取本地锁,但是本地锁已经被tx2持有了,这时候就是相互持有对方的锁(tx1持有全局锁并尝试获取本地锁,tx2持有本地锁并尝试获取全局锁),不过由于尝试获取全局锁是有尝试次数限制的(默认最多10次),所以tx2最终会获取全局锁超时失败,并释放本地锁,然后tx1得到本地锁从而完成二阶段的回滚(补偿)

 

 

读隔离

Seata(AT 模式)的默认全局隔离级别是 读未提交(Read Uncommitted)

如果业务确实需要读已提交,就需要使用@GlobalLock注解并使用select for update,  Seata的AT模式对当前读(select for update)进行了代理,如果加了@GlobalLock注解和使用select for update, 会进行获取全局锁的获取的重试。

出于总体性能上的考虑,Seata 目前的方案并没有对所有 SELECT 语句都进行代理,仅针对 FOR UPDATE 的 SELECT 语句。

 

 

 

 

 下面有待补充具体的实操

 

 

 

标签:回滚,Seata,本地,事务,模式,update,学习,提交,全局
From: https://www.cnblogs.com/huainanyin/p/18168640

相关文章

  • 提高学生学习成绩和自我效能感:护理培训的移动聊天机器人方法
    (Promotingstudents'learningachievementandself-efficacy:Amobilechatbotapproachfornursingtraining)DOI:10.1111/bjet.13158一、摘要研究目的:护理培训的目的不仅在于掌握技能,更在于培养解决问题的决策能力。然而,产科疫苗接种知识等培训项目大多采用以讲座为主......
  • 迁移学习讲解、举例基于resnet-50 backbone的YOLO v1模型
    一、概念介绍迁移学习是指通过将一个已经在某一任务上训练好的模型,应用于另一个任务上。在迁移学习中,一般会将预训练模型的权重加载到新的模型中,然后对新的模型进行微调。预训练模型是指在大规模的数据集上训练好的模型这些模型通常具有很好的泛化能力,可以应用于各种任务,如图......
  • 面向文献的学习
    文献相关内容的学习主要包含:如何查找文献、文献如何阅读、文献如何管理、如何做文献阅读笔记、如何引用文献。阅读英文文献用相关的翻译软件即可阅读文献的方式分粗读和精读最后还学习到了一个引用文献的技巧:那就是用自己的话表述别人的观点,换词但不能有语病。还发现了b站上有......
  • 网课-组合数学学习笔记
    排列\[A_n^m=\dfrac{n!}{(n-m)!}\]组合\[\dbinom{n}{m}=\dfrac{n!}{(n-m)!}\]下降幂&上升幂\[\]二项式定理隔板法如果隔板法的每个间隔有下界(下界可以不同),可以先把下界从整体减去。P5520[yLOI2019]青原樱:可将树看作隔板。环排列\(n\)的长度,\(m\)种颜色。可以......
  • Unity 热更--AssetBundle学习笔记 0.7
    AssetBundleAB包是什么?AssetBundle又称AB包,是Unity提供的一种用于存储资源的资源压缩包。Unity中的AssetBundle系统是对资源管理的一种扩展,通过将资源分布在不同的AB包中可以最大程度地减少运行时的内存压力,可以动态地加载和卸载AB包,继而有选择地加载内容。AB包的优势有哪些?......
  • Markdown学习
    markdown学习markdown学习markdown学习markdown学习markdown学习markdown学习markdown学习markdown学习markdown学习markdown学习markdown学习markdown学习markdown学习Markdown语法形式文本替换链接图片链接分割线语法这个比较简单,如果要创建分隔线,在单独......
  • xss-基于Pikachu的学习
    XSS漏洞-跨站脚本攻击XSS的原理跨站脚本攻击XSS(CrossSiteScripting),为了不和层叠样式表(CascadingStyleSheets,CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页面时,嵌入Web里面的Script代码会被执行,从而达到恶意攻击用......
  • 普冉PY32系列(十五) PY32F0系列的低功耗模式
    目录普冉PY32系列(一)PY32F0系列32位CortexM0+MCU简介普冉PY32系列(二)UbuntuGCCToolchain和VSCode开发环境普冉PY32系列(三)PY32F002A资源实测-这个型号不简单普冉PY32系列(四)PY32F002A/003/030的时钟设置普冉PY32系列(五)使用JLinkRTT代替串口输出日志普冉P......
  • Oracle日志记录模式 - LOGGING、FORCE LOGGING、NOLOGGING模式介绍
    一、日志记录模式(LOGGING、FORCELOGGING、NOLOGGING)内容转载自:https://www.cnblogs.com/tk-bolg/p/12867772.html1.三者的含义LOGGING:当创建一个数据库对象时将记录日志信息到联机重做日志文件。LOGGING实际上是对象的一个属性,用来表示在创建对象时是否记录REDO日志,包括......
  • JDK8生产参数的学习与整理
    JDK8生产参数的学习与整理背景五一期间加班整理一下用到的一些参数信息.希望能够继续巩固自己的知识.希望下次总结时能够添加上更多东西产品的启动时增加的参数-javaagent:./jmx_prometheus_javaagent-0.17.2.jar=8080:simple-config.yml-Xms20G-Xmx20G-Xmn10G......