67 mysql 的 间隙锁

embedded/2024/11/28 21:35:04/

前言

我们这里主要是 来看一下 mysql 中的 间隙锁

间隙锁 主要存在的地方一般就是在 查询主键查询不到, 索引查询查询不到 的场景

然后 我们这里来调试一下 这里的整个流程, 间隙锁的加锁 以及 间隙锁的使用, 以及 间隙锁的释放

从逻辑上来说 间隙锁 锁定的是一个区间, 按照我们常规的理解 他应该会保存 区间的起始地址, 但是 从实际的实现层面 mysql 这边的实现相当巧妙, 它是挂在比目标值大的下一条记录上面的, 比如主键有 1, 5, 10, 我们这里执行查询 “select * from tz_test_04 where id = 7 for update;” 间隙锁是挂在 id 为 10 的这条记录上面的一个行锁, 并且 增加了一个 GAP_LOCK 的标志, 用于 业务的判断

根据这个目标记录 10, 以及 10 的上一条记录 5, 改间隙锁推导出锁定的区间就是 (5, 10)

 

我们这里测试表结构如下 

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

 

测试数据如下, 之所以 留下一些区间 是为了产生 “间隙”

2127be77d3df032de8284a066f4957c4.png

 

 

添加间隙锁

为了演示, 我们这里 事务1 加锁 sql 如下 

此查询会在 id 为 10 的行上面增加一个行锁, 并打上 GAP_LOCK 标志, 逻辑意义上这个间隙锁锁定的区间为 主键的 (5, 10)

因此 我们在这个区间去 增加记录 会获取 行排他锁, 获取失败, 如果是 更新或者删除, 还不到获取行排他锁锁的地方, 因此可以正常执行 

begin;
select * from tz_test_04 where id = 7 for update;
-- sleep10min
commit;

 

获取锁的地方如下, mode 为 515 = LOCK_GAP + LOCK_X, 间隙排他锁 

锁定的记录的 heap_no 为 5, 上下文还有它的 space, page 的相关信息 

5768252738c719e082726aabf30ed507.png

 

在 search_mvcc 中上下文如下, 是在 cmp_dtuple_is_prefix_of_rec 的判断内部, 条件匹配不上的时候, 会添加这个间隙锁

会进入这里的比较, 我这边目前知道的有 等于 比较 

我们再来看一下 rec 的记录信息 

f086afcd6d9b9c7ff621dbc0cf434fad.png

 

目标记录的上下文信息如下, 可以看到上一次更新是 事务39531 处理的, 当前事务是 39537, 然后对应的记录是 id 为 10 的记录 

2d3c0feb0bc1f55ea7177c4188ae535e.png

 

 

间隙锁的使用

间隙锁区间新增记录

然后在 事务2 执行sql 如下, 然后可以看到 产生了阻塞

INSERT INTO `test_02`.`tz_test_04`(`id`, `field1`, `field2`) VALUES (8, 'field1', '1');

这里是判断 上下文是否有 存在冲突的锁是否被其他事务持有, 锁住的事务是 事务39537 即上面那副截图的事务, 然后当前事务是 事务39538, 是由 mysql 这边新分配的一个事务 

然后期望锁定的目标记录是 5 号记录, 结合上面的信息来看 是 id 为 10 的记录 

持有锁的事务的 mode 为 547 为 LOCK_GAP + LOCK_RECORD + LOCK_X

当前事务尝试获取的 mode 为 2563 为 LOCK_INSERT_INTENSION + LOCK_GAP + LOCK_X

0f03a97f0dbd501347d9ec6384a9db28.png

 

然后这里 持有锁的上下文 和 尝试获取锁的上下文 是不兼容的, 因此 当前 事务 只能挂起, 等待目标 事务 释放间隙锁

45efc2092d2d4213568ddabaf64c3c6c.png

 

我们再来看一下 这里的 heap_no 为什么会是 id 为 10 的记录的 heap_no 呢?

获取锁的时候是在插入数据记录的时候, 获取当前记录的下一条记录, 比如我们这里插入的记录 id 为 8, 那么是应该放在 id 为 5 和 id 为 10 的记录之间, 下一条记录即为 id 为 10 的记录 

然后 因此就巧妙的实现了 间隙锁, 锁是加载一个行记录上面的, 但是实际锁定的却是一个区间

ca3fa42ea6690c899c29270716418af3.png

 

 

间隙锁区间更新记录 

然后在 事务2 执行sql 如下, 然后可以看到 不会产生阻塞

update tz_test_04 set field1 = 'fieldUpdated' where id = 8;

这是因为如下判断锁是否兼容的时候, 这里在 间隙锁的第二个 case, 这里满足, 就是持有锁的事务是间隙锁, 并且当前事务没有 LOCK_INSERT_INTENSION, 我们这里是更新, 是满足条件的, 因此 视为兼容

844c9f7a0d09b437720d50c4750cc0e0.png

 

 

间隙锁区间删除记录 

然后在 事务2 执行sql 如下, 然后可以看到 不会产生阻塞

delete from tz_test_04 where id = 8;

这是因为如下判断锁是否兼容的时候, 这里在 间隙锁的第二个 case, 这里满足, 就是持有锁的事务是间隙锁, 并且当前事务没有 LOCK_INSERT_INTENSION, 我们这里是删除, 是满足条件的, 因此 视为兼容

8f1c29608ab65f2408572ae710c17c2e.png

 

 

间隙锁的释放

释放同样是在 事务提交 的时候

c86f62f2c489aeca0c701a7d6b034c62.png

 

 

间隙锁锁索引的情况

tz_test_04 的数据表数据如下, 这里更新了一下 id 为 10 的记录的 field1 的值, 让其在字符串层面增量排序 

5e54970477cc64363b4adcd7e025d424.png

 

执行 sql 如下 

begin;
select * from tz_test_04 where field1 = 'field7' for update;
-- sleep10min
commit;

 

走的索引查询, 同样如果是 索引字段 field1 没有命中的情况, 会基于索引记录 添加间隙锁

从 rec, 或者代码所在区域 可以看出当前 rec 实际上时索引记录, 然后下面会走 查询是否匹配, 匹配的话走聚簇索引的回表查询, 这里我们的 事务是 事务39576

2920c15bedffd64ffd3961461e8991c0.png

 

执行 sql 如下 “INSERT INTO `test_02`.`tz_test_04`(`id`, `field1`, `field2`) VALUES (8, 'field8', '8');”

会添加 数据记录, 索引记录, 这里阻塞是阻塞在插入索引记录的地方 

当前 事务39577 尝试获取 “field8” 的下一个索引记录 “field9” 的间隙锁 mode 为 2563, LOCK_INSERT_INTENSION + LOCK_GAP + LOCK_X

发现 事务39576 持有 “field9” 的间隙锁 mode 为 547, LOCK_GAP + LOCK_RECORD + LOCK_X

然后当前事务 有LOCK_INSERT_INTENSION 和 已有的间隙锁不兼容, 因此当前 事务 需要等待

37749970c849618d17f74dea8795e073.png

 

 

间隙锁锁非索引记录的情况

执行 sql 如下 

begin;
select * from tz_test_04 where field2 = '7' for update;
-- sleep10min
commit;

 

这个效果就转换为了 在每一行记录上面 加上行临键锁

上下文在这里, 具体遍历了每一行记录, 以及 supremum 

b9d1f2fc425a0eb34f26c6ef413948ab.png

 

遍历的各个记录如下, 下图是在 row_search_mvcc 中查看的 rec 的记录信息, 可以通过 field1 字段观察出那一条记录 

505fba1bb49bc044c871c7e4fcd2e1c1.png

 

 

间隙锁锁最大值到正无穷的情况

间隙锁锁在主键上面

执行 sql 如下

begin;
select * from tz_test_04 where id = 15 for update;
-- sleep10min
commit;

 

这里是遍历到了 supremum 记录, 然后这里会在 supremum 记录上面添加一个 行排他锁 

但是它的作用和在 supremum 记录上面增加一个 间隙锁的效果 是一样的 

7e608f7ea00914ab9dd9aa9c16ab2d3a.png

 

然后执行 sql 如下 “INSERT INTO `test_02`.`tz_test_04`(`id`, `field1`, `field2`) VALUES (15, 'fieldc', 'c');”

然后这里可以看到是在添加数据记录的时候尝试获取锁 mode 为 2563, LOCK_INSERT_INTENSION + LOCK_GAP + LOCK_X

然后已经持有的事务 mode 为 LOCK_RECORD + LOCK_X

然后 两个事务的情况不在下面的四种兼容情况内, 因此 本事务需要阻塞

需要注意的是这里在 supremum 记录上面增加的是 行排他锁, 而不是 间隙锁

d5d6900653879cfbfc624f637d307b19.png

 

 

间隙锁锁在索引上面

执行 sql 如下 

begin;
INSERT INTO `test_02`.`tz_test_04`(`id`, `field1`, `field2`) VALUES (8, 'fieldc', 'c');
-- sleep10min
commit;

 

这里是遍历到了 supremum 记录, 然后这里会在 supremum 记录上面添加一个 行排他锁 

但是它的作用和在 supremum 记录上面增加一个 间隙锁的效果 是一样的 

82fc7b9844cd456c479d0b2c4b36752d.png

 

然后执行 sql 如下 “INSERT INTO `test_02`.`tz_test_04`(`id`, `field1`, `field2`) VALUES (8, 'fieldc', 'c');”

然后这里可以看到是在添加索引记录的时候尝试获取锁 mode 为 2563, LOCK_INSERT_INTENSION + LOCK_GAP + LOCK_X

然后已经持有的事务 mode 为 LOCK_RECORD + LOCK_X

然后 两个事务的情况不在下面的四种兼容情况内, 因此 本事务需要阻塞

需要注意的是这里在 supremum 记录上面增加的是 行排他锁, 而不是 间隙锁

d004b72a1d39c378fa0cd42fa65ccd8d.png

 

 

 

 

 


http://www.ppmy.cn/embedded/141267.html

相关文章

mysql-分析并解决mvcc更新丢失问题

多版本并发控制(Multi-Version Concurrency Control, MVCC)是现代数据库系统中常用的一种并发控制机制,用于提高并发性能和数据一致性。然而,MVCC 本身并不能完全解决更新丢失问题。让我们详细探讨一下这个问题的原因和背景。 更…

大语言模型(LLM)的训练微调 Fine Tuning -- part3 本地调用

以下代码示范如何调用已经微调后的大语言模型,调用本地模型 先决条件 已经有了本地训练好的大语言模型,如何训练可以参考我的博文 《生成式 AI》课程 作业6 大语言模型(LLM)的训练微调 Fine Tuning -- part2-CSDN博客文章浏览阅…

IDEA全局设置-解决maven加载过慢的问题

一、IDEA全局设置 注意:如果不是全局设置,仅仅针对某个项目有效;例在利用网上教程解决maven加载过慢的问题时,按步骤设置却得不到解决,原因就是没有在全局设置。 1.如何进行全局设置 a.在项目页面,点击f…

计算机网络 第4章 网络层

计算机网络 (第八版)谢希仁 第 4 章 网络层4.2.2 IP地址**无分类编址CIDR**IP地址的特点 4.2.3 IP地址与MAC地址4.2.4 ARP 地址解析协议4.2.5 IP数据报的格式题目2:IP数据报分片与重组题目:计算IP数据报的首部校验和(不正确未改) …

【前端学习笔记】Web API——BOM与DOM

前置知识 JavaScript ECMAscript BOM DOM ECMAscript:相当于JS的执行标准。 BOM:浏览器对象模型。BOM 提供了与浏览器窗口交互的接口,这些接口独立于网页内容。BOM的对象允许JavaScript访问和操作浏览器窗口,是所有JavaScri…

上海迪士尼奇幻冬日巡游:IP营销如何出圈?

迪士尼,作为全球娱乐产业的巨头,一直以其卓越的IP营销能力闻名于世。而其推出的“迪士尼奇幻冬日巡游”,无疑再次展示了其深厚的营销功底和独特的IP魅力。上海迪士尼在品牌营销上如何出圈,吸引忠实消费者,成为了一个值…

蓝桥杯不知道叫什么题目

小蓝有一个整数,初始值为1,他可以花费一些代价对这个整数进行变换。 小蓝可以花贵1的代价将教数增加1。 小蓝可以花费3的代价将整数增加一个值,这个值是整数的数位中最大的那个(1到9) .小蓝可以花费10的代价将整数变为原来的2倍, 例如,如果整…

【论文笔记】Number it: Temporal Grounding Videos like Flipping Manga

🍎个人主页:小嗷犬的个人主页 🍊个人网站:小嗷犬的技术小站 🥭个人信条:为天地立心,为生民立命,为往圣继绝学,为万世开太平。 基本信息 标题: Number it: Temporal Grou…