63 mysql 的 行锁

前言

我们这里来说的就是 我们在 mysql 这边常见的 几种锁

行共享锁, 行排他锁, 表意向共享锁, 表意向排他锁, 表共享锁, 表排他锁

意向共享锁, 意向排他锁, 主要是 为了表粒度的锁获取的同步判断, 提升效率 

意向共享锁, 意向排他锁 这边主要的逻辑意义是数据表中是否有任意一行的行共享锁, 行排他锁被获取 

 

假设如下sql “select * from t_user_02 for update;”, 会首先会先尝试获取 t_user_02 的表意向排他锁, 然后再遍历符合条件的每一行记录, 获取每一行记录的 行排他锁

假设如下sql “select * from t_user_02 where id = ‘1’ for update;”, 会首先会先尝试获取 t_user_02 的表意向排他锁, 然后获取 id 为 ‘1’ 的记录的行排他锁

差距就在于 扫描表的记录, 前者需要扫描全表, 后者 只需要扫描 id = ‘1’ 的数据行 

之后 我们还会有一个锁粒度 的调试 

当然 这里需要区分一些情况, 比如 “select * from t_user_02;” 的查询是无锁查询, “select * from t_user_02 for update;” 是申请排他锁查询, “select * from t_user_02 lock in share mode;” 是申请共享锁查询 

在无锁查询的情况下, 是不会去尝试获取 表锁, 行锁 的, 是一直可以查询的 

 

测试数据表如下 

CREATE TABLE `t_user_02` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`name` varchar(24) DEFAULT NULL,`age` int(11) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8

 

t_user_02 的数据列表如下  

bd8be40e8b125f0914f17a5700af6362.png

 

 

表意向排他锁

我们这里调试 sql 如下 这里会先获取 t_user_02 的表意向排他锁, 然后再读取的时候在获取 id 为 2 的记录的行排他锁, 我们这里先看 表意向排他锁, 再看行排他锁 

begin;
select * from t_user_02 where id = '2' for update;
commit;

 

获取表意向锁这边是在 row_search_mvcc 中, 这里是属于读取记录之前, 会先尝试获取 t_user_02 的 表意向共享锁 或者 表意向独占锁

获取成功之后会继续往下走获取记录的相关业务流程

获取失败之后, 会挂起当前线程, 等待目标锁可以争取 然后再次尝试获取目标锁

在我们这里只存在 表意向共享锁, 表意向排他锁, 行共享锁, 行排他锁 的场景下面, 获取 表意向共享锁, 表意向排他锁 是恒成功的

73405b3cf1462babb67210241372ab61.png

 

获取锁这边处理如下, 判断是否有已经占用的 t_user_02 的表的锁 

如果没有可以直接获取给定的 表意向共享锁, 表意向独占锁

如果有判断已经持有的锁是否 和 当前请求的锁兼容, 在我们这里的场景下只考虑 表意向共享锁, 表意向独占锁, 行共享锁, 行独占锁 这里是几种都兼容 

表意向共享锁, 表意向排他锁主要是用于 表共享锁, 表排他锁的相关地方的提升效率的处理 

500f8ef6359b36de910a0462c9fb0017.png

 

然后 第一次迭代以后的迭代是不需要再获取 表意向共享锁, 表意向排他锁 了, 处理的地方如下 

第二次 以及以后的迭代, 走的是 ”if(!prebuilt->sql_stat_start)” 中的相关的流程了 

9f239e0b429ce3909fb26e2b83a2621c.png

 

 

表意向共享

我们这里调试 sql 如下 这里会先获取 t_user_02 的表意向共享锁, 然后再读取的时候在获取 id 为 2 的记录的行共享锁, 我们这里先看 表意向共享锁, 再看行共享锁 

begin;
select * from t_user_02 where id = '1' lock in share mode;
commit;

 

和上面获取 表意向共享锁 类似的流程, 只是这里获取的 表意向排他锁

在我们这里只存在 表意向共享锁, 表意向排他锁, 行共享锁, 行排他锁 的场景下面, 获取 表意向共享锁, 表意向排他锁 是恒成功的

9853db835ffd9a506efc49a656c56096.png

 

 

行排他锁

我们这里调试 sql 如下 这里会先获取 t_user_02 的表意向排他锁, 然后再读取的时候在获取 id 为 2 的记录的行排他锁, 我们这里来看 行排他锁 

begin;
select * from t_user_02 where id = '2' for update;
commit;

 

行共享锁的获取是在 获取了当前行的数据之后, 再来获取的 

select 中没有 “for update;”, “lock in share mode;” 的场景是 “prebuilt->select_lock_type == LOCK_NONE” 的场景 

我们这里是带 “for update”, 获取 行排他锁

1aa3138371aabdf9bb760ac09f6a4ed3.png

 

然后 其次就是尝试获取锁之后的处理, 获取成功之后 移动游标, 调用栈返回 

如果获取 行排他锁 不成功, 走 lock_wait_or_error, 等待目标锁可以争取 然后再次尝试获取目标锁

03e2be72acf38a978debc3dbb0b34a1e.png

 

行锁的实际这边如下, 分为 fastpath 和 slowpath

行锁这边是以 page 为单位的, 一个 page 公用一把锁, lock 中有 bitmap 来维护每一条记录的锁是否被占用, 以及其他信息 

“if(lock == null)” 这里是目标页还没有任何锁的情况, 直接创建锁, 获取锁, 这里可以直接响应 LOCK_REC_SUCCESS_CREATED, 是因为外层 lock_clust_rec_read_check_and_lock 有一个 lock_mutex_enter 有一个全局的同步 

然后下面就是 “else if(!impl)” 的处理是如果目标锁是关联当前事务, 尝试直接获取给定的记录的行锁, “lock_rec_set_nth_bit(lock, heap_no)” 就是获取目标记录的行锁

2b762881a9b5c745436fa2a82fb45700.png

 

我们再来看一下 slowpath

slowpath 这边主要是 fastpath 尝试获取锁失败的场景下面

如果目标记录已经被其他事务持有和当前目标锁冲突的锁, 则 DB_LOCK_WAIT, 上游 row_search_mvcc 走 wait 的流程 

否则 可以尝试获取目标锁, 然后响应给上游 获取锁成功

00fd7315fb23f1e9259469d600c1c987.png

 

行共享锁, 和 行排他锁这边的冲突规则主要如下

行共享锁 兼容于行共享锁, 行共享锁 不兼容于 行排他锁

行排他锁 不兼容于 行共享锁, 行排他锁 不兼容于 行排他锁

2e9613f68793704b2f09646b8a7613b1.png

 

 

共享

我们这里调试 sql 如下 这里会先获取 t_user_02 的表意向共享锁, 然后再读取的时候在获取 id 为 2 的记录的行共享锁, 我们这里看 行共享锁 

begin;
select * from t_user_02 where id = '1' lock in share mode;
commit;

 

和获取 行排他锁的流程基本上一致, 这里不多赘述 

f834e338650362795c0ecb7ef4ab5298.png

 

 

行锁锁索引字段的情况

我们这里更新测试表结构如下 

CREATE TABLE `tz_test_04` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`field1` varchar(128) DEFAULT NULL,`field2` varchar(128) DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE,KEY `field_1_2` (`field1`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8

 

然后执行 sql 如下 “select * from tz_test_04 where field1 = 'field5' for update;”

从 row_search_mvcc 这里的上下文可以看出当前 rec 是一条索引记录, 然后 后面走的流程 锁定的也是这部分满足条件的索引记录

因此 后面需要尝试获取索引记录的锁的情况, 如果锁不兼容, 则会阻塞 

这是在索引匹配 field1 = ‘field5’ 的记录上面增加的索引的 行临键锁, 然后 它还会在具体的数据记录上面增加 行排他锁, 在下一个索引记录上面增加 间隙锁

8a46afb99b01947742a59df0db812e43.png

 

在数据记录上面增加 行排他锁, 这里的 clust_rec 表示的是具体的数据记录, rec 表示是当前索引记录 

2d6fcb2407018bfc657fcab185e33464.png

 

遍历到不匹配 field1 = ‘field5’ 的第一个索引的地方, 在该记录上面增加了一个 间隙锁

c8381254fa12fe7ab9df47194c84cf67.png

 

因此如下 三个 sql 都会阻塞, 第一行是 获取索引的行排他锁 冲突, 第二行是 获取数据的行排他锁冲突, 第三行是与在索引 field1=’field9’ 上面的间隙锁冲突 

第四行, 第五行是与在索引 field1=’field5’ 上面的临键锁冲突 

select * from tz_test_04 where field1 = 'field5' for update;
select * from tz_test_04 where id = 5 for update;
INSERT INTO `test_02`.`tz_test_04`(`id`, `field1`, `field2`) VALUES (15, 'field8', '8');
INSERT INTO `test_02`.`tz_test_04`(`id`, `field1`, `field2`) VALUES (8, 'field3', '3');
INSERT INTO `test_02`.`tz_test_04`(`id`, `field1`, `field2`) VALUES (8, 'field5', '5');

 

 

行锁锁索引字段的情况

我们这里更新测试表结构如下 

CREATE TABLE `tz_test_04` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`field1` varchar(128) DEFAULT NULL,`field2` varchar(128) DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE,KEY `field_1_2` (`field1`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8

 

然后执行 sql 如下 “select * from tz_test_04 where field2 = '5' for update;”

如果是根据 非索引字段查询, 则会进行全表扫描, 会在所有的行上面增加 行临键锁

9f78054aebf05d76e451a46bbb88fd32.png

 

在 row_search_mvcc 中输出各个断点位置的 rec 信息如下, 即为各个 记录的信息

00a6e95cbf740638e11957947aae68cf.png

 

 

行排他锁阻塞的 N 种方式

假设我们这里尝试模拟 各种阻塞的方式, 事务1先进行执行, 然后事务2尝试获取行排他锁, 产生阻塞 

事务2 这边执行固定的 sql 语句如下 

begin;
select * from t_user_02 where id = '2' for update;
-- sleep 10min
commit;

 

事务1获取 表共享锁 导致 事务2 获取 MDL元数据锁 阻塞 

begin;
lock tables t_user_02 read;
-- sleep 10min
unlock tables;
commit;

 

事务1获取 表排他锁 导致 事务2 获取 MDL元数据锁 阻塞 

begin;
lock tables t_user_02 write;
-- sleep 10min
unlock tables;
commit;

 

事务1 获取行共享锁 导致 事务2 获取 行排他锁 阻塞

begin;
select * from t_user_02 where id = '2' lock in share mode;
-- sleep 10min
commit;

 

事务1 获取行排他锁 导致 事务2 获取 行排他锁 阻塞

begin;
select * from t_user_02 where id = '2' for update;
-- sleep 10min
commit;

 

 

共享锁阻塞的 N 种方式

假设我们这里尝试模拟 各种阻塞的方式, 事务1先进行执行, 然后事务2尝试获取行共享锁, 产生阻塞 

事务2 这边执行固定的 sql 语句如下 

begin;
select * from t_user_02 where id = '2' lock in share mode;
commit;

 

事务1获取 表排他锁 导致 事务2 获取 MDL元数据锁 阻塞 

begin;
lock tables t_user_02 write;
-- sleep 10min
unlock tables;
commit;

 

事务1 获取行排他锁 导致 事务2 获取 行排他锁 阻塞

begin;
select * from t_user_02 where id = '2' for update;
-- sleep 10min
commit;

 

 

where 1 = 1 for update 和 where id = 1 for update 的区别 

首先是都会获取 表意向排他锁

首先来看一下 “where 1 = 1 for update” 情况下的一个获取锁的处理

然后 我们这里讨论的主要是 行排他锁 的获取的差异

是会走 “else if (!impl)” 然后里面的这个 get + set

比如这里获取的是 第三条记录的 行排他锁, 会设置 bitmap 的第三位, 更新之后 (lock+1) 会变成 0x04 | 0x08 = 0x0c

这个获取了所有记录的行锁 逻辑意义上 等价于获取了表锁, 但是 实际的实现二者又是有一定的区别的

cc2fcf347a086d7c3be9833b1f012f59.png

 

同理, 这里是获取 第四条记录的 行排他锁 

这里从当前状态可以看到 (lock+1) 为 0x0c, 表示获取了 第二条记录, 第三条记录 的 行排他锁

本次更新调整了之后, (lock + 1) 会变成 0x0c | 0x10 = 0x1c

8a565ad5d5d7c3e7c7ebb48204df23cf.png

 

获取第五条记录的 行排他锁 的情况如下, 这里就不在继续 向下赘述了

631fa8948226edb86ee731b920ec58e4.png

 

 

再来看一下 “where id = 1 for update” 情况下的一个获取锁的处理

这里限定的是 id, 只会获取到一条记录, 因此这里只会走 RecLock 初始化的这部分处理

04a29159aa990bed9703ac650c4e3d5c.png

 

 

 

 

 

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

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

相关文章

江协科技STM32学习- P26 UART串口外设

🚀write in front🚀 🔎大家好,我是黄桃罐头,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流 🎁欢迎各位→点赞👍 收藏⭐️ 留言📝​…

使用 ADB 在某个特定时间点点击 Android 设备上的某个按钮

前提条件 安装 ADB:确保你已经在计算机上安装了 Android SDK(或单独的 ADB)。并将其添加到系统环境变量中,以便你可以在命令行中运行 adb。 USB调试:确保 Android 设备已启用 USB 调试模式。这可以在设备的“设置” -…

mint-ui Picker 显示异常

mint-ui Picker 显示异常 现象 最近一个老项目页面显示异常&#xff0c;使用mint-ui Picker显示异常,直接显示成了 数据对象&#xff0c;而不是具体travelName 字段 组件 mint-ui Picker 使用方式(vue方式) // template <mt-picker :slots"slots" value-key…

FastAPI性能对比:同步vs异步

大家好&#xff0c;FastAPI已成为构建Python API的最流行框架之一&#xff0c;因其速度和易用性而广受欢迎。但在构建高性能应用程序时&#xff0c;使用同步&#xff08;sync&#xff09;还是异步&#xff08;async&#xff09;代码执行是很重要的问题。本文将通过现实世界的性…

wx.setNavigationBarColor动态设置导航栏颜色无效(亲测有效)

wx.setNavigationBarColor动态设置导航栏颜色无效&#xff08;亲测有效&#xff09; 问题描述问题分析问题解决注意 问题描述 wx.setNavigationBarColor({frontColor: #E6E6E6,backgroundColor: #E6E6E6 })上面的代码设置后导航栏颜色没有变化&#xff0c;查看了app.json 以及…

Blender进阶:贴图与UV

9 UV 9.1 贴图与UV UV&#xff0c;指定每个面顶点在贴图上的坐标 演示&#xff1a; 1、添加物体 2、添加贴图&#xff0c;即图片纹理节点 3、进入UV Edit工作区 4、右边&#xff0c;选择一个面 5、左边&#xff0c;选择一个面&#xff0c;移动这个面 9.2 电子表格 电子…

利用LangChain与LLM打造个性化私有文档搜索系统

我们知道LLM&#xff08;大语言模型&#xff09;的底模是基于已经过期的公开数据训练出来的&#xff0c;对于新的知识或者私有化的数据LLM一般无法作答&#xff0c;此时LLM会出现“幻觉”。针对“幻觉”问题&#xff0c;一般的解决方案是采用RAG做检索增强。 但是我们不可能把…

PostgreSQL-06-入门篇-集合运算

文章目录 1. UNION 组合多个查询的结果集简介带有 ORDER BY 子句的 UNION设置样例表PostgreSQL UNION 示例1) 简单的 PostgreSQL UNION 示例2) PostgreSQL UNION ALL 示例3) 带 ORDER BY 子句 UNION ALL 示例 2. INTERSECT 取交集简介带 ORDER BY 子句的 INTERSECT 操作Postgre…

云计算作业二Spark:问题解决备忘

安装spark 教程源地址&#xff1a;https://blog.csdn.net/weixin_52564218/article/details/141090528 镜像下载 教程给的官网下载地址很慢&#xff0c;https://archive.apache.org/dist/spark/spark-3.1.1/ 这里的镜像快很多&#xff1a; 清华软件源&#xff1a;https://mi…

(51)MATLAB迫零均衡器系统建模与性能仿真

文章目录 前言一、迫零均衡器性能仿真说明二、迫零均衡器系统建模与性能仿真代码1.仿真代码2.代码说明3.迫零均衡器zf_equalizer的MATLAB源码 三、仿真结果1.信道的冲击响应2.频率响应3.迫零均衡器的输入和输出 前言 使用MATLAB对迫零均衡器系统进行建模仿真&#xff0c;完整的…

【C#】编写计算机选课程序

文章目录 一、引言二、程序概述三、程序设计四、 界面设计五、代码实现六、为每一个选项添加事件七、事件处理八、完成展示 一、引言 在这篇文章中&#xff0c;我将介绍如何开发一个简单的计算机选课程序。这个程序将允许学生根据自己的需求选择不同等级的课程&#xff0c;并即…

【AIGC】AI工作流workflow实践:构建日报

workflow实践 引言实现步骤分析实践创建 dify workflow 应用创建工作流内部节点1、设置输入字段2、创建两个LLM节点3、设置结束节点 运行工作流 结语 引言 工作流 workflow 是现在 LLM 很重要的一个概念&#xff0c;因为对于一个模型来说&#xff0c;非常复杂的问题很难一次性…

无人机飞手考证热,装调检修技术详解

随着无人机技术的飞速发展和广泛应用&#xff0c;无人机飞手考证热正在持续升温。无人机飞手不仅需要掌握飞行技能&#xff0c;还需要具备装调检修技术&#xff0c;以确保无人机的安全、稳定和高效运行。以下是对无人机飞手考证及装调检修技术的详细解析&#xff1a; 一、无人机…

034_Structural_Transient_In_Matlab结构动力学问题求解

结构动态问题 问题描述 我们试着给前面已经做过的问题上加一点有趣的东西。 结构静力学求解 当时求解这个问题&#xff0c;在最外面的竖直切面加载了一个静态的固定的力。下面我们试试看在上方的表面增加一个脉冲压力载荷。 采用统一的有限元框架&#xff0c;定义问题&…

江协科技STM32学习- P23 DMA 直接存储器存取

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

「Math」高等数学知识点大纲(占位待处理)

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「C/C」C/C程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasoli…

linux-UART

参考博客 https://blog.csdn.net/m0_38106923/article/details/126024970?sharetypeblog&shareId126024970&sharereferAPP&sharesourceweixin_40933496&sharefromlink 1.串口 UART的全称是Universal Asynchronous Receiver and Transmitter&#xff0c;即异步…

mac如何下载 测试旧版chrome兼容问题

mac安装低版本的chrome 下载地址&#xff1a; Download older versions of Google Chrome for Windows, Linux and Mac 下载需要模拟的浏览器版本 记住版本号 1、下载后安装 安装时提醒 保留两者 2、可能会提醒无法验证 3、设置允许 就可以打开 4、打开后发现还是新版本的浏…

【软服之家-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

数据治理,数据提取,大数据中心建设,大数据治理总体解决方案书(word,ppt原件)

1. 数据管理的现状 2. 数据治理的概述 1.1数据治理概念 2.2数据治理目标 3. 数据治理体系 4. 数据治理核心领域 1.1 数据模型 1.2 数据生命周期 &#xff08;1&#xff09;数据生成及传输 &#xff08;2&#xff09;数据存储 &#xff08;3&#xff09;数据处理和应用…