【Seata】03 - Seata AT 模式全局锁相关知识简单整理

文章目录

    • 前言
    • 参考目录
    • 版本说明
    • 分析整理
      • 1、全局锁的引入说明
      • 2、全局锁相关源码整理
      • 2.1、流程简图
      • 2.2、事务分支注册前的 SQL 相关操作
      • 2.3、注册分支(获取全局锁)

前言

上一篇文章介绍了 AT 模式的调用流程,但是有个比较重要的概念没有提及到,所以这一篇来展开说明一下——全局锁。

参考目录

  • Seata 官方文档
  • Seata 官方 Demo :seata-xa
  • Seata AT 模式

版本说明

由于官方 Demo 版本较低,本文使用的版本如下:

  • SeataV1.7.0
  • druid-spring-boot-starterV1.2.16

分析整理

1、全局锁的引入说明

在官方文档对于 AT 模型的篇章中有进行关于读写隔离的示例说明,这其中就涉及到了全局锁。

(截图自官方文档)在这里插入图片描述

在 Demo 里面业务主要是进行了写隔离,因此下面的分析也是以此为基础进行展开。

上图简明扼要提出了全局锁的获取时机以及应用场景,下面结合源码来看下。

2、全局锁相关源码整理

2.1、流程简图

上篇文章有根据输出记录按照时序步骤绘制了流程图,对关键的信息进行提取后加上全局锁的内容,绘制了新的流程图:

在这里插入图片描述

简单说明一下:

  • 这张简图主要是事务分支相关的操作,因此没有绘制 TM 相关的内容。
  • 虚线方框内是同一个方法的操作。
  • 主要的方法也一并贴出来方便后续查找。

2.2、事务分支注册前的 SQL 相关操作

这一部分在上一篇文章也有提及,这里再简单带过一下。

在分支注册前,回先执行业务逻辑,生成前后镜像,再贴一下堆栈信息图:

在这里插入图片描述

io.seata.rm.datasource.exec.AbstractDMLBaseExecutor#executeAutoCommitFalse

在这里插入图片描述

得到前后镜像是为了生成 UndoLog 方便后续回滚操作。SQL 执行之后还没有 Commit,需要获取到全局锁并且在数据库表中写入 UndoLog 后才会提交。

完成这一步骤之后,继续分支事务操作,方法 io.seata.rm.datasource.ConnectionProxy#processGlobalTransactionCommit 完成了三个重要步骤:

  1. 注册分支(包含获取全局锁)
  2. undo_log 记录插入
  3. SQL 提交

在这里插入图片描述

本文的分析要点集中在步骤 1 中,对于步骤 2 上一篇博客已经进行了详细的分析,也贴出了相关的 UndoLog 内容,本文就不再进行说明。

2.3、注册分支(获取全局锁)

来到 Client 端(RM)注册分支的底层方法:

io.seata.rm.AbstractResourceManager#branchRegister

在这里插入图片描述

来到 Server 端(TC)的分支注册方法:

io.seata.server.coordinator.AbstractCore#branchRegister

在这里插入图片描述

该方法的主要逻辑:

  1. 使用 xid 获取到全局会话。
  2. 锁定会话并在事务上下文进行操作。
  3. 全局会话状态检查。
  4. 全局会话添加会话生命周期监听器。
  5. 新建全局事务分支,获得分支会话对象。
  6. 锁定分支会话。
  7. 将分支会话添加到全局会话中,如果出现异常,解锁分支并抛出异常。
  8. 打印日志并返回分支id。

下面来看看底层是如何实现的:

branchSessionLock(globalSession, branchSession);

io.seata.server.transaction.at.ATCore#branchSessionLock

在这里插入图片描述

根据打印的日志记录来看,应用数据 applicationData 是 null,因此直接进入到子方法。

io.seata.server.session.BranchSession#lock

在这里插入图片描述

io.seata.server.lock.AbstractLockManager#acquireLock

在这里插入图片描述

这个方法会从分支会话中获取所有行锁信息。

// get locks of branch
List<RowLock> locks = collectRowLocks(branchSession);

io.seata.server.lock.AbstractLockManager#collectRowLocks

在这里插入图片描述

上图以其中一个事务分支为例对方法进行了简单的逻辑梳理。该方法根据分支注册请求信息构建了锁对象集合并返回。

顺便说一下,transactionId 实际上就是 xid 中 IP 端口后面的那一长串数字:

  • xid:192.168.2.117:8091:2252230319988862988
  • transactionId:2252230319988862988
  • branchId:2252230319988862990

在这里插入图片描述

回到上一层继续往下深入获取锁。

io.seata.server.storage.db.lock.DataBaseLocker#acquireLock

在这里插入图片描述

io.seata.server.storage.db.lock.LockStoreDataBaseDAO#acquireLock
在这里插入图片描述

这是获取全局锁的主要方法,方法逻辑如下:

  1. 首先,该方法获取数据库连接 conn,并设置自动提交属性为 false。同时,创建 PreparedStatement 对象 ps 和 ResultSet 对象 rs,以及一个用于存储已存在行键的 HashSet 集合 dbExistedRowKeys。

  2. 如果传入的锁对象列表 lockDOs 的大小大于1,则通过过滤去除重复的行键,将其赋值给 lockDOs。这样可以确保锁对象列表中不会存在重复的行键。

  3. 如果 skipCheckLock 参数为 false,说明需要进行锁的检查。即先查询数据库中的锁表,判断是否有其他事务正在持有或正在回滚相同的全局锁。

    • 创建一个查询检查锁的 SQL 语句 checkLockSQL,并将锁对象数量作为参数传递给该语句。
    • 通过 PreparedStatement 对象 ps 执行 checkLockSQL,并将锁对象的行键作为参数设置到 PreparedStatement 对象中。
    • 执行查询并遍历 ResultSet 对象 rs。对于每一行记录,检查是否存在其他事务持有相同的全局锁。
      • 如果存在其他事务持有相同的全局锁,则设置 canLock 标志为 false,并终止循环。
      • 同时,将查询到的数据库中已存在的行键添加到 dbExistedRowKeys 集合中。
    • 如果 canLock 标志为 false,表示无法获取锁,执行回滚操作,并根据 autoCommit 参数和锁的状态判断是否需要抛出异常。
    • 如果 dbExistedRowKeys 集合不为空,表示锁在数据库中已存在,因此需要将这些行键从 lockDOs 列表中移除。
  4. 如果经过锁的检查后,lockDOs 列表为空,表示锁已全部存在于数据库中,无需再次获取锁,直接提交事务并返回 true。

  5. 接下来,执行锁的获取操作。如果 lockDOs 列表中只有一个锁对象,调用 doAcquireLock 方法进行单个锁的获取。如果获取失败,执行回滚操作,并返回 false。

  6. 如果 lockDOs 列表中存在多个锁对象,调用 doAcquireLocks 方法进行批量锁的获取。如果获取失败,执行回滚操作,并返回 false。

  7. 最后,提交事务,关闭 ResultSet、PreparedStatement 对象,根据原始的自动提交属性设置连接的 autoCommit 属性,并关闭数据库连接。

io.seata.server.storage.db.lock.LockStoreDataBaseDAO#doAcquireLock
在这里插入图片描述

获取到全局锁后,会把锁信息保存到数据库表 lock_table 中,在释放锁后记录也会删除。

(完)

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

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

相关文章

Virtual File System了解

虚拟文件系统(以下简称vfs)是置于具体文件系统之上的抽象层&#xff0c;指定内核和具体文件系统的接口。允许client以统一方式访问不同的具体文件系统。比如可以通过VFS透明访问本地硬盘和网络设备就像在同一个设备访问一样。 superblock 每个挂载的文件系统都由vfs superblo…

Node.js 中间件是怎样工作的?

express自带路由功能&#xff0c;可以侦听指定路径的请求&#xff0c;除此之外&#xff0c;express最大的优点就是【中间件】概念的灵活运用&#xff0c;使得各个模块得以解耦&#xff0c;像搭积木一样串起来就可以实现复杂的后端逻辑。除此之外&#xff0c;还可以利用别人写好…

Redisson—独立节点模式和集群管理工具

一、集群管理工具 Redisson集群管理工具提供了通过程序化的方式&#xff0c;像redis-trib.rb脚本一样方便地管理Redis集群的工具。 1、 创建集群 以下范例展示了如何创建三主三从的Redis集群。 ClusterNodes clusterNodes ClusterNodes.create() .master("127.0.0.1:…

(其他) 剑指 Offer 46. 把数字翻译成字符串 ——【Leetcode每日一题】

❓ 剑指 Offer 46. 把数字翻译成字符串 难度&#xff1a;中等 给定一个数字&#xff0c;我们按照如下规则把它翻译为字符串&#xff1a;0 翻译成 “a” &#xff0c;1 翻译成 “b”&#xff0c;……&#xff0c;11 翻译成 “l”&#xff0c;……&#xff0c;25 翻译成 “z”。…

springcloud3 注册中心以及cloud启动原理总结(含面试)

一 Springcloud微服务面试题 1.1 为何使用注册中心 1)问题描述 在多个单体微服务之间&#xff0c;可以直接通过http请求进行通信&#xff0c;但是存在以下问题&#xff1a; 1.调用服务提供者时需要写ip和端口&#xff0c;如果出现ip和端口进行了修改&#xff0c;没有及时告…

微信小程序开发---基本组件的使用

目录 一、scroll-view &#xff08;1&#xff09;作用 &#xff08;2&#xff09;用法 二、swiper和swiper-item &#xff08;1&#xff09;作用 &#xff08;2&#xff09;用法 三、text &#xff08;1&#xff09;作用 &#xff08;2&#xff09;使用 四、rich-tex…

【面试经典150题】H 指数

题目链接 给你一个整数数组 citations &#xff0c;其中 citations[i] 表示研究者的第 i 篇论文被引用的次数。计算并返回该研究者的 h 指数。 根据维基百科上 h 指数的定义&#xff1a;h 代表“高引用次数” &#xff0c;一名科研人员的 h 指数 是指他&#xff08;她&#x…

C# 中什么是重写(子类改写父类方法)

方法重写是指在继承关系中&#xff0c;子类重新实现父类或基类的某个方法。这种方法允许子类根据需要修改或扩展父类或基类的方法功能。在面向对象编程中&#xff0c;方法重写是一种多态的表现形式&#xff0c;它使得子类可以根据不同的需求和场景提供不同的方法实现。 方法重…

智慧公厕是将数据、技术、业务深度融合的公共厕所敏捷化“操作系统”

文明社会的进步离不开公共设施的不断创新和提升。而在这些公共设施中&#xff0c;公共厕所一直是一个备受关注和改善的领域。近年来&#xff0c;随着智慧城市建设的推进&#xff0c;智慧公厕成为了城市管理的重要一环。智慧公厕不仅仅是为公众提供方便和舒适的便利设施&#xf…

如何使用Puppeteer进行金融数据抓取和预测

导语 Puppeteer是一个基于Node.js的库&#xff0c;可以用来控制Chrome或Chromium浏览器&#xff0c;实现网页操作、截图、PDF生成等功能。本文将介绍如何使用Puppeteer进行金融数据抓取和预测&#xff0c;以及如何使用亿牛云爬虫代理提高爬虫效果。 概述 金融数据抓取是指从…

Java HashMap源码学习

Java HashMap源码学习 基本使用 包含创建&#xff0c;添加&#xff0c;删除&#xff0c;迭代&#xff0c;打印 val map java.util.HashMap<Int, Int>() map.put(1, 2) map.put(2, 2) map.put(3, 2) map.remove(1) map.forEach {println("it.key${it.key}, it.va…

Cocos独立游戏开发框架中的日志模块:Bug无所遁形

引言 本系列是《8年主程手把手打造Cocos独立游戏开发框架》&#xff0c;欢迎大家关注分享收藏订阅。 在Cocos独立游戏开发框架中&#xff0c;一个强大的日志模块是不可或缺的组成部分。日志不仅仅是记录应用程序的运行状态&#xff0c;还可以用于故障排除、性能监测和安全审计…

26.篮球练习

题目 Description 小徐酷爱打篮球&#xff0c;在小学期的前两周半都在练习篮球。 今天&#xff0c;小徐想要练习如何突破。练习场地可由如下所示的网格图表示&#xff0c;图中的位置可用坐标表示。 其中A点(0,0)为小徐的起始位置&#xff0c;B点(n,m)为小徐想要到达的位置。…

高教社杯数模竞赛特辑论文篇-2018年D题:汽车总装线配置的优化模型研究

目录 摘 要 1. 问题的重述 1.1 装配要求 1.2 喷涂要求 1.3 需要解决的问题 2. 问

python中如何使用正则表达匹配\本身?(文末赠书)

点击上方“Python爬虫与数据挖掘”&#xff0c;进行关注 回复“书籍”即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 将军向宠&#xff0c;性行淑均。 大家好&#xff0c;我是皮皮。 一、前言 前几天在Python钻石群【空】问了一个Python正则表达式的问题&#xff0c;一起…

PageHelper分页原理解析

大家好&#xff0c;我是Leo! 今天给大家带来的是关于PageHelper原理的解析&#xff0c;最近遇到一个SQL优化的问题&#xff0c;顺便研究了一下PageHelper的原理&#xff0c;毕竟也是比较常用&#xff0c;源码也比较好看的懂&#xff0c;如果感兴趣的小伙伴可以跟着过程去DEBUG源…

tcp字节传输(java)-自定义包头和数据识别

1、背景 tcp传输的时候会自动拆包&#xff0c;因此服务端接收的数据段可能跟客户端发送过来的数据段长度不一致&#xff0c;比如客户端一次发送10000个字节。但是服务端接收了两次才接收完整&#xff08;例如第一次接收6000字节&#xff0c;第二次接收4000字节&#xff09;。但…

vue3 watch watchEffect

watch & watchEffect 函数都是监听器, 用于监视数据的变化; watch 有惰性&#xff0c;watchEffect 无惰性&#xff1b;watch 需要指定具体的监视属性&#xff0c;watchEffect 不需要指定具体的监视属性和配置参数&#xff0c;会自动感知代码依赖&#xff1b;watch 能获取到…

【Web_接口测试_Python3_日期时间库】Arrow获取过去/当前未来时间日期、格式化时间日期、转换时间戳、获取不同时区时间日期等

## 简介 Arrow是一个 Python 库&#xff0c;它提供了一种明智且人性化的方法来创建、操作、格式化和转换日期、时间和时间戳。它实现并更新 datetime 类型&#xff0c;填补了功能上的空白&#xff0c;并提供了支持许多常见创建方案的智能模块 API。简而言之&#xff0c;它可以帮…

AI云服务平台大全:GPU租用 | App托管 | MLOps平台

我们搜集整理了国内外主要的深度学习云服务商&#xff0c;包括云GPU供应商、WebApp托管商和MLOps平台商。 推荐&#xff1a;用 NSDT编辑器 快速搭建可编程3D场景 1、云GPU供应商 只有一台笔记本电脑&#x1f4bb;不足以运行你的AI模型&#xff0c;忘记它吧&#xff0c;使用云 …