分布式事务seata之AT与TCC模型

news2024/11/8 20:44:39

1. seata分布式事务简介

seata是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。

Seata提供了AT、TCC、SAGA和XA事务模型,为用户打造一站式的分布式解决方案。

简单来说,Seata就是针对主流事务解决方案的封装,我们直接使用Seata这个框架就可以选择性的使用不同的事务模型来解决分布式事务问题。

2. seata中的AT事务模型

AT模式实际上是2PC协议的一种演变方式,也是通过两个阶段的提交或者回滚来保证多节点事务的一致性,它的工作模型如下图所示。

具体工作流程说明如下:

 1. 第一阶段, 应用系统会把一个业务数据的事务操作和回滚日志记录在同一个本地事务中提交,在提交之前,会向TC(seata server)注册事务分支,并申请针对本次事务操作的表的全局锁。 接着提交本地事务,本地事务会提交业务数据的事务操作以及UNDO LOG,放在一个事务中提交。

2. 第二个阶段,这一个阶段会根据参与到同一个XID下所有事务分支在第一个阶段的执行结果来决定事务的提交或者回滚,这个回滚或者提交是TC来决定的,它会告诉当前XID下的所有事务分支,提交或者回滚。
  • 如果是提交, 则把提交请求放入到一个异步任务队列,并且马上返回提交成功给到TC,这样可以避免阻塞问题。而这个异步任务,只 需要删除UNDO LOG就行,因为原本的事务已经提交了。
  • 如果是回滚,则开启一个本地事务,执行以下操作
    • 通过XID和Branch ID查找到响应的UNDO LOG记录
    • 数据校验,拿到UNDO LOG中after image(修改之后的数据)和当前数据进行比较,如果有不同,说明数据被当前全局 事务之外的动作做了修改,这种情况需要根据配置策略来做处理。
    • 根据UNDO LOG中的before image和业务SQL的相关信息生成并执行回滚语句
    • 提交本地事务,并把本地事务的执行结果上报给TC

 2.1 AT模式原理详解

假设存在一个业务表product,表结构如下:

FieldTypeKey
idbigint(20)PRI
namevarchar(100)
sincevarchar(100)

这个表中,有一条对应的数据:

idnamesince
1TXC2024

 假设AT模式下的其中一个分支事务的执行业务逻辑对应的sql语句如下:

update product set name = 'GTS' where name = 'TXC';

 在Seata的AT模式下,执行的过程如下:

AT模式第一阶段

这个阶段主要分为两个过程。

  1. 执行数据的修改
  2. 记录回滚日志

具体过程如下:

1. 解析前面的 Update 语句,得到SQL类型(UPDATE)、表(Product)、条件(where name ='TXC')等相关信息。
2. 根据解析到的条件信息,生成一条查询语句用来查询修改之前的数据状态
select id , name , since from product where name = 'TXC'
3. 执行上述的业务SQL,也就是更新 name GTS
4. 在根据第二个步骤查询的主键 id 定位修改后的数据
select id, name, since from product where id = 1;
5. 有了更新前后的数据,以及业务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"
}
6. UNDO日志提交之前,会向TC(SEATA-SERVER)注册分支事务,并申请 product 表中,主键值为1记录的全局锁。
7. 业务数据的更新语句(UPDATE)和UNDO LOG一起提交,并将本地事
务的提交结果上报到TC。

 AT模式第二阶段

这个阶段,主要是TC会根据各个分支事务的执行结果,来决定事务的提交或者回滚。

1. 如果所有分支事务的执行结果都正常,则提交事务。由于实际上各个本地事务在第一阶段已经提交了,所以只需要异步去删除当前事务分支对应UNDO LOG表中的记录即可。
2. 如果存在部分事务分支执行异常的情况,则需要对事务进行回滚,回滚步骤如下

  • 收到TC的分支回滚请求,事务参与者开启一个本地事务。
  • 通过XID和BranchID查找到UNDO LOG中对应的记录
  • 拿到数据后,先对数据进行校验,使用UNDO LOG中 afterImages(修改后的数据) 和当前 product 表中的数据进行比较,如果发现数据不相同,说明数据被当前全局事务之外的程序修改过,这种情况需要根据配置的策略来进行处理
  • 根据UNDO LOG中的 beforeImages(修改之前的数据) 和业务SQL相关信息生成回滚语句并执行。
update product set name = 'TXC' where id = 1;
3. 提交本地事务,并把本地事务的执行结果(分支事务的回滚结果)上报给TC。

总结: 从seata的整体思路上,类似于把数据库的事务在作用范围内作了一层升华,核心步骤和SQL类似:

  • 加锁
  • 写事务日志
  • 提交事务或回滚事务

思考: 那么在这种事务模型下,它的事务隔离级别是如何实现的呢?

2.2 AT模式的事务隔离级别

2.2.1 写隔离

所谓的写隔离,就是多个事务对同一个表的同一条数据做修改的时候,需要保证对于这个数据更新操作的隔离性,在传统事务模型中,我们一般是采用锁的方式来实现。

那么在分布式事务中,如果存在多个全局事务对于同一个数据进行修改,为了保证写操作的隔离,也需要通过一种方式来实现隔离性,自然也是用到锁的方法,具体来说

  • 在第一阶段本地事务提交之前,需要确保先拿到全局锁,如果拿不到全局锁,则不能提交本地事务
  • 拿到全局锁的尝试会被限制在一定范围内,超出范围会被放弃并回滚本地事务并释放本地锁。

 举一个具体的例子,假设有两个全局事务tx1和tx2,分别对a表的m字段进行数据更新操作,m的初始值是1000.

1. tx1先开始执行,按照AT模式的流程,先开启本地事务,然后更新m=1000-100=900。在本地事务更新之前,需要拿到这个记录的全局锁。
2. 如果tx1拿到了全局锁,则提交本地事务并释放本地锁。
3. 接着tx2后开始执行,同样先开启本地事务拿到本地锁,并执行m=900-100的更新操作。在本地事务提交之前,先尝试去获取这个记录的全局锁。而此时tx1全局事务还没提交之前,全局锁的持有者是tx1,所以tx2拿不到全局锁,需要等待
4.   接着, tx1在第二阶段完成事务提交或者回滚,并释放全局锁。此时tx2就可以 拿到全局锁来提交本地事务。当然这里需要注意的是,如果tx1的第二阶段是 全局回滚,则tx1需要重新获取这个数据的本地锁,然后进行反向补偿更新实 现事务分支的回滚。
5.   此时,如果tx2仍然在等待这个数据的全局锁并且同时持有本地锁,那么tx1 的分支事务回滚会失败,分支的回滚会一直重试直到tx2的全局锁等待超 时,放弃全局锁并回滚本地事务并释放本地锁之后,tx1的分支事务才能最终 回滚成功.

tx2等待全局锁图示:

 tx2超时放弃全局锁,tx1拿到本地锁后回滚图示:

在整个过程中,全局锁在tx1结束之前一直被tx1持有,所以并不会发生脏写问题

2.2.2 读隔离

seata(AT模式)的默认全局隔离级别是读未提交。如果在特定场景下,必须要求全局的读已提交,目前seata的方式只能通过SELECT FOR UPDATE语句来实现。

SELECT FOR UPDATE语句的执行会申请全局锁,如果全局锁被其它事务持有,则释放本地锁(回滚SELECT FOR UPDATE语句的本地执行)并重试。这个过程中,查询是被block住的,直到全局锁拿到,即读取的相关数据是已提交的,才返回。

3. seata TCC模式

seata 的TCC事务和2pc的思想类似, 但并不是2pc的实现,TCC不再是两阶段提交,而只是它对事务的提交/回滚是通过执行一段confirm/cncel业务逻辑来实现,并且也没有全局事务来把控整个事务逻辑。

它的本质上是一种补偿的思路,它把事务运行过程分成Try、confirm/cancel两个阶段,每个阶段由业务代码控制,这样事务的锁力度可以完全自由控制。

  • 一阶段 prepare 行为, 通过业务代码编排来调用try接口进行资源预留
  • 二阶段 commit 或 rollback 行为.
    • 当所有事务参与者的try接口都成功了,那么意味着事务管理器可以 提交事务,于是调用每个事务参与者的confirm接口实现真正的业务 提交操作
    • 如果事务参与者中任意一个参与者出现异常,则调用每个参与者的 cancel接口进行数据回滚。

seata框架会把每组TCC接口当作一个Resource,成为Tcc Resource. 这组TCC接口可以是RPC、也可以是服务内的JVM调用。

当业务服务启动时,Seata框架会自动扫描并且识别到TCC接口的调用方和发布方。

1. 如果是TCC接口发布方,那么在业务启动的时候会向TC注册TCC Resource,每个资源会带一个资源ID
2. 如果是TCC接口调用方,Seata框架会给调用方增加一个切面,在运行的时候,这个切面会拦截所有对TCC接口的调用。每调用一次 Try 接口,切面会先向TC注册一个分支事务,然后再去执行原来的RPC调用。
当请求链路调用完成后,TC通过分支事务的资源ID回调到正确的参与者
去执行对应TCC资源的Confirm或者Cancel方法。

3.1 TCC接口设计与异常控制

TCC模式下,需要我们根据自己的业务场景分别实现Try、Confirm和cancel三个操作

TCC三个方法描述:

  • Try: 资源的检测和预留
  • Confirm: 执行的业务操作提交;要求Try成功 Confirm一定要能成功;
  • Cancel: 预留资源释放

所以如果我们要接入到TCC,最重要的是考虑如何把自己的业务拆分成两个阶段来实现。

比如以资金扣减这类场景为例,在接入TCC之前,对某个账户扣款,只需要 一条更新账户余额的SQL就可以完成。但是在接入到TCC之后,我们就需要考虑把这样一个步骤拆分成两个阶段,实现三个方法,并且如果一阶段 Try 成功的情况下,要保证二阶段 Confirm 一定能成功。

如下图所示,针对TCC的事务设计,我们把扣减的逻辑拆分成两个步骤

 1. try,需要先检查账户余额以及预留本次要扣减的资金,所以预留的方式就是通过冻结该账户的转账资金,被冻结的资金无法被其他事务使用!

2. confirm,真正执行扣钱的操作,这个阶段使用 try 阶段冻结的资金进行扣款,总余额真正发生了变化。
3. cancel,如果是回滚,则把 try 冻结的资金加回来,回到初始状态。

 因此,TCC相比AT模式来说,对代码侵入性较大,但是不需要像AT模式那样获取全局行锁,所以性能方面会比AT模式高一些

3.2 TCC模式允许空回滚

所谓空回滚,就是在TCC分布式事务中,如果没有调用TCC资源的try方法的情况下,直接调用了第二阶段的cancel方法。因此在设计cancel方法的时候,需要识别当前是一个空回滚,然后直接返回。

那么什么情况下会出现空回滚呢?看下面这个图。

Try未执行、cancel执行了

 在seata中,注册分支事务是在调用RCP的时候进行的,也就是seata的切面会拦截这次请求,先向TC注册一个分支事务,然后再去执行RPC调用逻辑。

假设RPC调用逻辑有问题,比如出现网络异常、或者目标服务宕机的情况下,会导致RPC调用失败。也就是没有执行Try方法,但是由于分布式事务已经开启了,Seata需要把事务推进到最终状态,所以 ,TC会回调参与者第二阶段的cancel接口,形成空回滚问题。

思考: 这个问题需要在设计的时候考虑到吗?

我们可以想办法在cancel方法中识别到空回滚,然后不做任何操作直接 返回成功就行。而关键在于如何识别这是一次空回滚

实现思路是需要直到第一阶段是否执行,如果执行了就正常回滚,没执行就是空回滚。因此我们可以用一张额外的事务控制表,记录分布式事务的ID和分支事务ID

然后在第一个阶段Try里面插入一条记录,表示第一个阶段执行了。

当cancel方法调用时,再根据全局事务和分支事务id去匹配是否存在该记录,如果存在说明执行过可以正常回滚。

3.3 TCC模式下的幂等控制

幂等性在TCC模式下也需要重点考虑,因为TCC要确保第二阶段的执行成功,会去重试请求confirm或cancel接口,比如下面这个场景

 从图中可以看到,提交或者回滚是TC到事务分支的网络调用,因此如果出现网络故障,游客了嗯会出现参与者已经提交了,但是TC没有收到返回结果的情况。

这个时候TC就会重复调用Confirm或Cancel接口,直到成功!

所以,在这种情况下需要考虑Confirm和Cancel这两个接口的幂等性,那么如何设计幂等性解决方案呢?

其实这里面比较好的方案是通过状态机的方式来实现,我们可以创建一个事 务控制表,然后保存当前这个分支事务的执行状态,比如初始化、已提交、 已回滚等。
  • 当执行try方法的时候,意味着这个事务的状态是初始化。
  • 在第二阶段中执行confirm或者cancel时,修改成已提交或者已回滚。
因为一个数据的状态只会出现一次,并且状态是不可逆的,所以当指定状态已经出发一次修改之后,再通过该状态修改同一条数据时,就无法修改成功 了

3.4 TCC模式下的防悬挂问题

在TCC事务模式中,还有一种可能的情况

第二阶段的Cancel方法可能比Try方法先执行,因为允许空回滚的存在,所以Cancel方法会认为Try方法还没 有被执行,因此空回滚直接返回成功。
对于Seata来说,它认为TCC中第二阶段的Cancel已经执行成功了,所以认为整个分布式事务也就结束了。
但是有可能出现等到Seata认为分布式事务结束并且也执行了Cancel空回滚之后,Try方法才正式开始执行,去执行资源的预留。
当出现这种情况的时候,分布式事务第一阶段预留的业务资源就再也没有人能够处理,从而导致这个资源一直被挂着,我们称这种现象为悬挂!

对于这种情况,我们需要考虑到: 允许空回滚,但是不允许空回滚之后再执行Try操作。

 为解决这个问题,我们需要考虑到try执行时,要判断cancel方法是否已经执行过了,如果已经执行了,那么try方法就不应该执行。因此,问题的关键,在于,try怎么知道cancel执行过了?

其实还是可以通过前面提到的事务控制表来解决幂等性的方法来解决

当执行try方法时,会去判断事务控制表里面是否有存在已回滚状态的事务记录,如果有,则不执行try。

4. 总结

分布式事务seata主要就是为了解决多服务下的数据一致性问题,提供了AT、TCC、SAGA、XA四种事务模型。本文主要讲述了AT模式和TCC模式,其思路都是借鉴的2PC模式,AT模式下采用的是全局事务锁来保证各分支事务的数据一致性,TCC模型,则是采用3个核心方法,通过业务代码来控制分布式事务。需要注意的是,在分布式架构中,一定要考虑到由于网络通信导致的问题,从而避免隐藏bug。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1819106.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Unity:Text-TextMeshPro 不显示中文

共计四步: 一、去C盘复制一份字体: C:\Windows\Fonts二、粘贴到你的项目里(任意文件位置),得到“MSYH”: 三、右键字体文件,依次点击create–>TextMeshPro–>FontAsset: …

visdom使用时所遇的问题及解决方法

最近在用visdom进行可视化的过程中,虽然可有效的避免主机拒绝访问(该问题的解决方法,请参考深度学习可视化工具visdom使用-CSDN博客)即在终端输入python -m visom.server 1.训练过程中visdom出现ValueError: too many file descr…

科技项目验收测试必须进行吗?软件测试公司推荐

科技项目验收测试是指在科技项目开发周期中,对项目完成后进行的一种测试和评估工作。它的目的是验证项目是否达到预期的要求,并确保项目交付给客户前达到预期的质量标准。 一、科技项目验收测试的必要性   科技项目验收测试是项目管理中不可或缺的一个…

5.5 Python 迭代器与生成器

文章目录 1. 三元表达式1.1 格式1.2 示例1.3 嵌套 2. 生成式2.1 列表生成式2.2 字典生成式2.3 集合生成式2.4 元组生成式 3. 可迭代对象4. 迭代器4.1 迭代器的优缺点4.2 迭代器的惰性机制4.3 生成迭代器4.4 文本IO包装器4.5 字符串迭代器4.6 列表迭代器4.7 字典键迭代器4.8 元组…

怎么改图片尺寸更方便?在线图片改大小的使用方法

图片怎么快速改尺寸呢?在网上传图或者做其他用途时,经常会对图片的尺寸有要求,当拍摄或者制作的图片太大或者太小时,都会导致图片的无法正常使用,那么就需要按照规定将图片改大小之后才能正常使用。 在遇到图片修改大…

ui自动化中,隐式等待和显示等待什么时候使用

隐式等待 在页面刷新加载时,页面元素还没有出来,这个时候如果去找元素就会找不到报错 或者点了一个菜单,页面加载时 用笨办法,就是用sleep等待固定的时间,这种浪费的时间比较多,就可以用隐式等待&#xf…

酷暑骄阳,热情似火丨deepin校园联盟走进湖北大学,共话开源新篇章

内容来源:deepin(深度)社区 炎炎夏日,骄阳似火,6月11日,deepin校园联盟湖北大学站交流活动如期举行。在湖北大学计算机与信息工程学院,deepin(深度)社区研发工程师王溢学为热情似火的学子们开展…

打造地下管廊智慧监测解决方案-守护城市生命线|计讯物联

方案背景 地下综合管廊是城市基础设施的关键部分,即在城市地下建造一个隧道空间,将电力、通信,燃气、供热、排水等各种工程管线集于一体,被视为城市的“生命线”。随着城市化进程的加快,地下管线的数量、种类和密度不断…

动态规划:最长重复子数组

本题不算难,但是如果直接想dp数组怎么定义的话就会头晕,先想递推公式的含义就知道为什么需要冗余的dp[0][0]了 class Solution {public int findLength(int[] nums1, int[] nums2) {int res 0;//1.确定dp数组含义int[][] dp new int[nums1.length1][nu…

不懂产品和工艺的小白能学好FMEA吗?

在制造业的广阔天地里,FMEA(Failure Modes and Effects Analysis,失效模式与影响分析)一直被视为质量控制的关键武器。然而,很多人认为只有懂产品和工艺的行家才能玩转FMEA,对于小白来说似乎遥不可及。今天…

智慧环保一体化平台简介

据悉,环保问题日益受到人们的关注,智慧环保一体化平台作为解决环保问题的有力工具,正逐渐走进人们的视野。朗观视觉智慧环保一体化平台通过整合各类环保资源,实现环境数据的实时监测、分析与管理,为环境保护提供智能化…

windows10蓝屏原因自检记录

背景 跑训练的过程中电脑自动重启了,为了满足长期训练的需要,研究一下死机原因 查看系统日志 右键我的电脑——管理——系统工具——事件查看器——系统 检索级别为错误的内容,在死机时间附近查找相关日志,发现如下日志 计算机…

【第六篇】SpringSecurity的权限管理

一、权限管理的实现 服务端的各种资源要被SpringSecurity的权限管理控制可以通过注解和标签两种方式来处理。 放开了相关的注解后在Controller中就可以使用相关的注解来控制了 JSR250注解 /*** JSR250*/ @Controller @RequestMapping("/user") public class UserC…

深度!千字长文拆解AI Agent ,浅谈国内AI Agent 现状

“现如今ChatGPT的热度逐渐褪去,而字节的扣子逐渐被越来越多的国人知晓,钉钉、百度、Dify等也纷纷涉足工作流功能,打开国内AI工作流to B市场。今天学姐将用通俗易懂的方式讲解AI Agent是什么,以及国内大厂做Agent的进展。大家不要…

机器人中roll/pitch/yaw angles 和Euler angles的区别

在机器人学和航空领域,roll、pitch、yaw angles(滚转角、俯仰角、偏航角)和Euler angles(欧拉角)是两个经常出现的概念,它们用于描述物体在三维空间中的姿态或方向。以下是对这两个概念的解释以及它们之间的…

DeepSpeed Monitoring Comm. Logging

Monitoring 支持多种后端:Tensorboard、WandB、Comet、CSV文件; TensorBoard例子: 自动监控:DeepSpeed自动把重要metric记录下来。只需在配置文件里enable相应的看板后端即可: {"tensorboard": {"enabl…

园区数字化运营管理平台的优势、价值有哪些?

​随着全球及我国数字经济的蓬勃发展,数字化转型已成为各行各业提升效率和竞争力的关键。园区作为区域经济发展的重要载体,数字化运营管理平台更是成为了推动园区经济高质量发展的重要工具。 随着园区数字化运营管理平台的而引入,为园区在运…

FISCO BCOS x GitLink,为国产开源技术生态注入新活力

作为中国领先的区块链底层平台之一,FISCO BCOS 自成立以来始终致力于推动国产开源区块链技术的应用和普及。近期,FISCO BCOS 将开源代码托管到CCF官方代码托管平台 GitLink (确实开源),为国产开源技术生态注入新活力。…

github 本地仓库上传及报错处理

一.本地文件上传 这里为上传部分,关于gitbash安装配置,读者可自行搜索,由于已经安装完成,未进行截图保存,这里便不做赘述。 1.登录git账号并创建一个仓库 点击仓库打开后会看到这个仓库的网址链接(这个链…

SAP 价格金额信息控制

1)migo / mb51/mb51/mb59 关于价格和金额的显示,需要权限控制,当权限对象F_BKPF_BUK,检查对应【公司代码】 和 ACTVT有03’的权限,如果没有03的权限,对应行的价格和金额显示为空 关于价格和金额的显示,需要权限控制&a…