首页 > 其他分享 >分布式事务(六):Seata之AT模式原理

分布式事务(六):Seata之AT模式原理

时间:2024-02-07 17:23:45浏览次数:23  
标签:回滚 Seata 事务 name 阶段 提交 全局 分布式

1、整体机制

  Seata AT模型,基于本地ACID事务的关系型数据库实现。

两阶段提交协议机制如下:

  一阶段:业务数据和回滚日志在同一个本地事务中提交,释放本地锁和连接资源

  二阶段:提交异步化,非常快速地完成;回滚通过一阶段的回滚日志进行反向补偿

2、事务状态

  global_table是seata的全局事务表,可以通过global_table表中status字段知悉全局事务处于哪个状态。

2.1、全局事务状态表

状态 码值 备注
全局事务开始(Begin) 1 此状态可以接受新的分支事务注册
全局事务提交中(Committing) 2 这个状态会随时改变
全局事务提交重试(CommitRetry) 3 在提交异常被解决后尝试重试提交
全局事务回滚中(Rollbacking) 4 正在重新回滚全局事务
全局事务回滚重试中(RollbackRetrying) 5 在全局回滚异常被解决后尝试事务重试回滚中
全局事务超时回滚中(TimeoutRollbacking) 6 全局事务超时回滚中
全局事务超时回滚重试中(TimeoutRollbackRetrying) 7 全局事务超时回滚重试中
异步提交中(AsyncCommitting) 8 异步提交中
二阶段已提交(Committed) 9 二阶段已提交,此状态后全局事务状态不会再改变
二阶段提交失败(CommitFailed) 10 二阶段提交失败
二阶段决议全局回滚(Rollbacked) 11 二阶段决议全局回滚
二阶段全局回滚失败(RollbackFailed) 12 二阶段全局回滚失败
二阶段超时回滚(TimeoutRollbacked) 13 二阶段超时回滚
二阶段超时回滚失败(TimeoutRollbackFailed) 14 二阶段超时回滚失败
全局事务结束(Finished) 15 全局事务结束
二阶段提交超时(CommitRetryTimeout) 16 二阶段提交因超过重试时间限制导致失败
二阶段回滚超时(RollbackRetryTimeout) 17 二阶段回滚因超过重试时间限制导致失败
未知状态(UnKnown) 0 未知状态

2.2、分支事务状态表

状态 码值 备注
分支事务注册(Registered) 1 向TC注册分支事务
分支事务一阶段完成(PhaseOne_Done) 2 分支事务一阶段业务逻辑完成
分支事务一阶段失败(PhaseOne_Failed) 3 分支事务一阶段业务逻辑失败
分支事务一阶段超时(PhaseOne_Timeout) 4 分支事务一阶段处理超时
分支事务二阶段已提交(PhaseTwo_Committed) 5 分支事务二阶段提交
分支事务二阶段提交失败重试(PhaseTwo_CommitFailed_Retryable) 6 分支事务二阶段提交失败重试
分支事务二阶段提交失败不重试(PhaseTwo_CommitFailed_Unretryable) 7 分支事务二阶段提交失败不重试
分支事务二阶段已回滚(PhaseTwo_Rollbacked) 8 分支事务二阶段已回滚
分支事务二阶段回滚失败重试(PhaseTwo_RollbackFailed_Retryable) 9 分支事务二阶段回滚失败重试
分支事务二阶段回滚失败不重试(PhaseTwo_RollbackFailed_Unretryable) 10 二阶段提交失败
未知状态(UnKnown) 0 未知状态

2.3、全局事务超时回滚中(TimeoutRollbacking)

  当某个seata全局事务执行过程中,无法完成业务。

  TC中的一个定时任务(专门用来寻找已超时的全局事务),发现该全局事务未回滚完成,就会将此全局事务改为全局事务超时回滚中(TimeoutRollbacking),开始回滚,直到回滚完毕后删除global_table数据。

  当全局事务处于该状态,请排查为何业务无法在限定时间内完成事务。若确实无法完成,应调大全局事务超时时间。(如排查一切正常,请检查tc集群时区与数据库是否一致,若不一致请改为一致)。

3、写隔离

3.1、写隔离原理

  一阶段本地事务提交前,需要确保先拿到全局锁;

  拿不到全局锁,不能提交本地事务;

  拿全局锁的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁。

3.2、提交回滚原理

  两个全局事务tx1和tx2,分别对 a 表的 m 字段进行更新操作,m的初始值1000。

3.2.1、事务提交原理

  tx1先开始,开启本地事务,拿到本地锁,更新操作 m = 1000 - 100 = 900。本地事务提交前先拿到该记录的全局所,本地提交释放本地锁。

  tx2后开始,开启本地事务,拿到本地锁,更新操作 m = 900 - 100 = 800。本地事务提交前,尝试拿该记录的全局锁,tx1全局提交前,该记录的全局锁被tx1持有,tx2需要重试等待 全局锁。

 0

  tx1二阶段全局提交,释放全局锁;tx2 拿到全局锁 提交本地事务。

3.2.2、事务回滚原理

  若tx1的二阶段全局回滚,则tx1需要重新获取该数据的本地锁,进行反向补偿的更新操作,实现分支的回滚。此时,如果tx2仍在等待该数据的 全局锁,同时持有本地锁,则tx1的分支回滚会失败。tx1分支的回滚会一直重试,直到tx2的全局锁 等锁超时,放弃全局锁并回滚本地事务释放本地锁,tx1的分支回滚最终成功。

 

0

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

4、读隔离

  数据库本地事务隔离级别 读已提交(Read Committed) 或 以上的基础上,Seata (AT模式)的默认全局隔离级别是 读未提交(Read Uncommitted)。

  若应用在特定场景下,必须要求全局的读已提交,Seata2.0的方式是通过 SELECT FOR UPDATE 语句的代理。

 0

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

  Seata2.0目前的并没有对所有的SELECT语句进行代理,仅针对 FOR UPDATE 的 SELECT 语句。

5、工作机制

用如下示例表示AT分支的工作过程:

  业务表 product:

Field Type Key
id bigint(20) PRI
name varchar(100)  
since varchar(100)  

  AT 分支事务的业务逻辑:

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

5.1、一阶段

1、解析SQL:得到SQL的类型(UPDATE)、表(product)、条件(where name = 'TXC')等相关的信息;

2、查询前镜像:根据解析得到的条件信息,生成查询语句,定位数据;

select id, name, since from product where name = 'TXC';

得到前镜像:

id name since
1 TXC 2014

3、执行业务SQL:更新这条记录的name为'GTS';

4、查询后镜像:根据前镜像的结果,通过 主键 定位数据;

select id, name, since from product where id = 1;

得到后镜像:

id name since
1 GTS 2014

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、提交前,向 TC 注册分支:申请 product 表中,主键值等于1的记录的全局锁;

  7、本地事务提交:业务数据的更新和前面步骤中生成的UNDO LOG一并提交;

  8、将本地事务提交的结果上报给TC。

5.2、二阶段

5.2.1、回滚

  1、收到 TC 的分支回滚请求,开启一个本地事务,执行如下操作;

  2、通过 XID 和 Branch ID查找到相应的 UNDO LOG 记录;

  3、数据校验:拿 UNDO LOG 中的后镜与当前数据进行比较,如有不同,说明数据被当前全局事务之外的动作做了修改。

  4、根据 UNDO LOG 中的前镜像和业务SQL的相关信息生成并执行回滚的语句:

update product set name = 'TXC' where id = 1;

  5、提交本地事务,并把本地事务的执行结果(即分支事务回滚的结果)上报给TC。

5.2.2、提交

  1、收到 TC 的分支提交请求,把请求放入一个异步任务的队列中,马上返回提交成功的结果给TC;

  2、异步任务阶段的分支提交请求将异步和批量地删除相应 UNDO LOG 记录。

5.3、回滚日志表

CREATE TABLE `undo_log` (  
    `id` bigint(20) NOT NULL AUTO_INCREMENT,  
    `branch_id` bigint(20) NOT NULL,  
    `xid` varchar(100) NOT NULL,  
    `context` varchar(128) NOT NULL,  
    `rollback_info` longblob NOT NULL,  
    `log_status` int(11) NOT NULL,  
    `log_created` datetime NOT NULL,  
    `log_modified` datetime NOT NULL,  
    PRIMARY KEY (`id`),  
    UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

 

标签:回滚,Seata,事务,name,阶段,提交,全局,分布式
From: https://www.cnblogs.com/RunningSnails/p/18011084

相关文章

  • 分布式事务(四):Seata之Saga事务模式原理
    Saga模式是SEATA提供的长事务解决方案,在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。  Seata2.x提供的Saga是基于状态机引擎实现的,下面来看看状态机引擎。1、状态机......
  • 分布式事务(五):Seata之XA事务模式原理
    1、XA事务模式概述XA事务模式是在Seata定义的分布式事务框架内,利用事务资源(数据库、消息服务等)对XA协议的支持,以XA协议的机制来管理分支事务的一种事务模式。 1.1、执行阶段可回滚:业务SQL操作放在XA分支中进行,由资源对XA协议的支持来保证可回滚持......
  • 分布式事务(三):Seata之TCC事务模式原理
    TCC模式是Seata支持的一种由业务方细粒度控制的侵入式分布式事务解决方案,是继AT模式后第二种支持的事务模式。其分布式事务模型直接作用于服务层,不依赖底层数据库,可以灵活选择业务资源的锁定粒度,减少资源锁持有时间,可扩展性好,是为独立部署的SOA服务而设计的。TCC......
  • 分布式事务(二):Seata概述
    Seata官方文档地址:https://seata.apache.org/zh-cn/。1、Seata引入 用户购买商品的业务逻辑。整个业务逻辑由3个微服务提供支持:仓储服务:对给定的商品扣除仓储数量。订单服务:根据采购需求创建订单。帐户服务:从用户帐户中扣除余额。在上述架构图中有仓......
  • pytorch 多机单卡分布式训练配置笔记.18010304
    pytorch多机单卡分布式训练配置笔记记录通过torchrun进行pytorch的分布式训练配置方法,示例代码为基本的分布式训练框架代码,无实际功能环境操作系统:Ubuntu22.04Python环境:anaconda23.11.0、Python3.8pytorch:2.1.2编写代码将代码保存为main.py模型训练代码写到train函数......
  • tensorflow 2.x 多机单卡 分布式训练配置笔记.18010232
    tensorflow2.x多机单卡分布式训练配置笔记tensorflow2.x多机单卡demo代码演示。配置笔记多机多卡属于tensorflow的tf.distribute.MultiWorkerMirroredStrategy策略,下面为详细的环境配置和demo代码环境、版本操作系统:Ubuntu22.04Python环境:anaconda23.11.0、Python......
  • Jmeter事务控制器
    事务控制器(TransactionController),事务控制器生成一个额外的示例,该示例测量执行嵌套测试元素所花费的总时间GenerateParentSample:生成父例,如果选中则该样本将作为其他样本的父样本生成,如果不选中则该样本将作为独立样本生成Includedurationoftimerandpre-postprocess......
  • 数据库之隔离级别,脏读幻读,事务特性
    目录1事务隔离级别1.1默认隔离级别1.2读未提交1.3读已提交1.4可重复读1.5序列化2事务关键词2.1定义(脏读,不可重复读,虚读)2.2不可重复读与幻读的区别3事务的四个特性3.1原子性3.2一致性3.3隔离性3.4持久性1事务隔离级别1.1默认隔离级别ISOLATION_DEFAULT:默认......
  • 为什么要用redis分布式锁
    为什么我们做分布式使用Redis? 绝大部分写业务的程序员,在实际开发中使用Redis的时候,只会SetValue和GetValue两个操作,对Redis整体缺乏一个认知。这里对 Redis常见问题做一个总结,解决大家的知识盲点。 1、为什么使用Redis 在项目中使用Redis,主要考虑两个角度:......
  • 分布式事务Seata
    TC (TransactionCoordinator)-事务协调者:维护全局和分支事务的状态,驱动全局事务提交或回滚。TM (TransactionManager)-事务管理器:定义全局事务的范围,开始全局事务、提交或回滚全局事务。RM (ResourceManager)-资源管理器:管理分支事务处理的资源(Resource),与TC......