当前位置: 首页 > news >正文

Seata RM的事务提交与回滚源码解析

文章目录

  • 前言
  • 一、RM提交事务
  • 二、RM回滚事务
    • 2.1、undo校验逻辑
    • 2.2、执行回滚逻辑
  • 总结
      • RM 的事务提交与回滚行为说明(基于 Seata AT 模式)
        • 1. 提交阶段(Phase Two Commit)
        • 2. 回滚阶段(Phase Two Rollback)


前言

  当TC接收到TM的提交/回滚请求后,在处理好自身相关的逻辑后,会向每一个分支事务RM发送请求,驱动RM执行实际的提交/回滚业务逻辑:
在这里插入图片描述
  微服务端处理对应请求,是在io.seata.rm.AbstractRMHandler中:
在这里插入图片描述

一、RM提交事务

  DataSourceManagerbranchCommit是处理事务提交的方法:
在这里插入图片描述


  调用的是asyncWorker的方法,AsyncWorkerDataSourceManager一个用于异步处理任务的组件:
在这里插入图片描述
  在AsyncWorker的构造中,会创建一个固定大小的线程池执行doBranchCommitSafely定时任务。
在这里插入图片描述


在这里插入图片描述
  最终会调用到addToCommitQueue方法,首先尝试将任务放入队列的尾部,如果队列已满,会返回false,不会抛出异常。然后会通过CompletableFuture异步编排机制,先通过scheduledExecutor执行doBranchCommitSafely的逻辑,然后再次执行addToCommitQueue
  如果当前的队列已满commitQueue.offer(context)返回的就是false。会先从队列中消费元素,然后再尝试将本次任务加到队列中。那如果队列未满,也就是commitQueue.offer(context)返回的是true。那么就直接return了,任务什么时候会执行?在上文中提到,构造AsyncWorker时,会启动一个定时任务调用addToCommitQueue方法。
在这里插入图片描述
  所以重点需要关注doBranchCommitSafely的逻辑,而该方法最终会调用doBranchCommit,在该方法中:

  1. 使用drainTo()方法将commitQueue中当前所有任务一次性取出,放入allContexts列表中。
  2. 把取出来的Phase2Context 根据resourceId进行分组。
  3. 遍历每一组同一资源 ID 下的提交任务,调用 =dealWithGroupedContexts()逐组提交,实际提交过程发生在dealWithGroupedContexts() 方法中。
    在这里插入图片描述
      在得到数据库代理连接对象,以及undolog后,执行删除undolog并且提交事务的逻辑:
    在这里插入图片描述
      删除undolog,并且提交分支事务,如果出现异常,则回滚,并且调用addAllToCommitQueue再次放入队列。

这里为什么要调用addAllToCommitQueue再次放入队列?
因为这里的提交事务,以及操作,都是针对删除undolog表的,不是针对业务代码的。业务代码事务的提交,是在执行业务代码时的。TC驱动RM提交事务,此时执行的操作是处理undolog,而不是提交业务代码的事务。也就不会存在业务代码存在bug,重试一百次依旧失败的场景。这里的 重试仅仅针对删除undolog失败的场景,捕获的异常类型也仅仅是SQL异常。
但是当前 addAllToCommitQueue() 没有内置重试次数限制,所以如果某条记录永远删不掉,会进入“无限重试”风险;实际部署中需要搭配告警机制(日志报警、慢提交监控)来识别这种问题;

在这里插入图片描述

二、RM回滚事务

  DataSourceManagerbranchRollback是处理事务回滚的方法:
在这里插入图片描述
  在undo方法中,会根据当前分支id和xid,去查询对应的undolog。
在这里插入图片描述
  执行回滚相关逻辑的核心代码:undoExecutor.executeOn(connectionProxy);
在这里插入图片描述

2.1、undo校验逻辑

  在executeOndataValidationAndGoOn方法中,会对前置镜像和后置镜像进行校验,这里涉及到脏数据回滚:
在这里插入图片描述
在这里插入图片描述
  在dataValidationAndGoOn方法中,首先会取出前置镜像后置镜像。然后进行比较:前置镜像和后置镜像相同,说明数据没有发生修改,也就不需要回滚。
在这里插入图片描述
  然后会获取数据库中的当前记录,和后置镜像比较:

  • 如果当前记录和后置镜像相同,证明没有其他方法对该条数据进行修改。所以可以回滚。
  • 如果当前记录和后置镜像不相同,还需要进行判断:
    • 前置镜像和当前数据相同,证明虽然当前记录被其他方法修改了,但是修改后的值和前置镜像,也就是执行事务之前的值是一样的,相当于变相地已经回滚了,所以无需再次回滚。
    • 前置镜像和当前数据不相同,这种情况证明当前记录被其他方法修改了,不能回滚。比如某样商品原本的库存是1000,本次事务将其改成了700,但是另一个方法手动将其改成了800。那么现在读取到的数据是800,和前置镜像后置镜像都不一样,这种情况不能简单地回滚到1000。

在这里插入图片描述

2.2、执行回滚逻辑

  undoPST.executeUpdate();会执行回滚的逻辑:
在这里插入图片描述

  然后会执行deleteUndoLog删除undolog表:
在这里插入图片描述
在这里插入图片描述

总结

RM 的事务提交与回滚行为说明(基于 Seata AT 模式)

1. 提交阶段(Phase Two Commit)

在全局事务的一阶段中,业务 SQL 已经通过本地事务提交完成,Spring 的事务已结束。随后,RM 通过异步机制清理事务日志:

  • 实际操作:删除 undo_log 表中与本次事务相关的日志记录。
  • 实现机制:
    • 使用阻塞队列存储待处理的提交上下文(Phase2Context);
    • 借助定时任务线程池(AsyncWorker)周期性调度任务;
    • 结合 CompletableFuture 异步编排机制,确保即使提交队列满时也能自动重试。
  • 容错设计:若日志删除失败,将记录异常,并自动重试。删除失败不会影响业务数据的一致性。

2. 回滚阶段(Phase Two Rollback)

若全局事务在协调器(TC)层被判定为失败,RM 将执行回滚操作。回滚行为基于一阶段写入的 undo_log 执行“反向补偿”,具体流程如下:

  1. 读取 undo_log 中记录的前镜像和后镜像
  2. 获取当前数据库的实时数据
  3. 进行数据一致性校验,决定是否执行回滚,判断逻辑如下:
前镜像 vs 当前数据后镜像 vs 当前数据处理方式
相同相同不执行回滚(数据未被修改)
不同相同执行回滚
不同不同抛出异常(数据已被篡改)
相同不同不执行回滚(幂等性处理)

注:若当前数据与后镜像不同,但与前镜像一致,说明业务重试或幂等行为已覆盖,回滚可安全跳过。

http://www.xdnf.cn/news/264151.html

相关文章:

  • 六大机器学习算法全解析:企业级开发实战与深度理解
  • AWS云服务深度技术解析:架构设计与最佳实践
  • Android Compose 物联网(IoT)UI 组件库封装指南
  • Dev-C++下载安装使用教程
  • 单细胞测序数据分析流程的最佳实践
  • Java学习手册:关系型数据库基础
  • 爬虫准备前工作
  • 【AI面试准备】NLP解析API文档生成测试脚本
  • 二叉树 - JS - 2
  • 49-dify案例分享-私有化 MCP 广场搭建与网页小游戏智能体工作流实战
  • 学习Cesium自定义材质
  • 硬件工程师面试常见问题(12)
  • 【LeetCode Hot100】贪心篇
  • 在pycharm profession 2020.3将.py程序使用pyinstaller打包成exe
  • Windows 中使用dockers创建指定java web 为镜像和运行容器
  • C++ STL vector高级特性与实战技巧
  • AVFormatContext 再分析零
  • 在Windows系统中使用Docker发布镜像到镜像仓库
  • 用PyTorch搭建卷积神经网络实现MNIST手写数字识别
  • 生成式 AI 的工作原理
  • Elasticsearch 中的索引模板:如何使用可组合模板
  • 【在Spring Boot中集成Redis】
  • 【赵渝强老师】TiDB生态圈组件
  • 3D人物关系图开发实战:Three.js实现自动旋转可视化图谱(附完整代码)
  • 人工智能助力工业制造:迈向智能制造的未来
  • 别样健康养生之道
  • AI 与生物技术的融合:开启精准医疗的新纪元
  • ros2 humble 控制真实机械臂(以lerobot为例)
  • 一种基于重建前检测的实孔径雷达实时角超分辨方法——论文阅读
  • **Java面试大冒险:谢飞机的幽默与技术碰撞记**