67 mysql 的 间隙锁

news2024/11/28 23:15:57

前言

我们这里主要是 来看一下 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.coloradmin.cn/o/2249361.html

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

相关文章

小米PC电脑手机互联互通,小米妙享,小米电脑管家,老款小米笔记本怎么使用,其他品牌笔记本怎么使用,一分钟教会你

说在前面 之前我们体验过妙享中心,里面就有互联互通的全部能力,现在有了小米电脑管家,老款的笔记本竟然用不了,也可以理解,毕竟老款笔记本做系统研发的时候没有预留适配的文件补丁,至于其他品牌的winPC小米…

Apache Zeppelin:一个基于Web的大数据可视化分析平台

今天给大家推荐一下 Apache Zeppelin,它是一个基于 Web 的交互式数据接入、数据分析、数据可视化以及协作文档 Notebook,类似于 Jupyter Notebook。 Apache Zeppelin 支持使用 SQL、Java、Scala、Python、R 等编程语言进行数据处理和分析,同时…

彻底理解如何保证ElasticSearch和数据库数据一致性问题

一.业务场景举例 需求: 一个卖房业务,双十一前一天,维护楼盘的运营人员突然接到合作开发商的通知,需要上线一批热门的楼盘列表,上传完成后,C端小程序支持按楼盘的名称、户型、面积等产品属性全模糊搜索热门…

EasyExcel: 结合springboot实现表格导出入(单/多sheet), 全字段校验,批次等操作(全)

全文目录,一步到位 1.前言简介1.1 链接传送门1.1.1 easyExcel传送门 2. Excel表格导入过程2.1 easyExcel的使用准备工作2.1.1 导入maven依赖2.1.2 建立一个util包2.1.3 ExcelUtils统一功能封装(单/多sheet导入)2.1.4 ExcelDataListener数据监听器2.1.5 ResponseHelper响应值处理…

前端实用知识-用express搭建本地服务器

目录 一、为什么会有这篇文章? 二、使用前的准备-如环境、工具 三、如何使用?-express常用知识点 四、代码演示-配合截图,简单易懂 一、为什么会有这篇文章? 在日常前端开发中,我们离不开数据,可能是用…

Redis(概念、IO模型、多路选择算法、安装和启停)

一、概念 关系型数据库是典型的行存储数据库,存在的问题是,按行存储的数据在物理层面占用的是连续存储空间,不适合海量数据存储。 Redis在生产中使用的最多的是用作数据缓存。 服务器先在缓存中查询数据,查到则返回,…

C#基础控制台程序

11.有一个54的矩阵,要求编程序求出其中值最大的那个元素的值,以及其所在的行号和列号。 12.从键盘输入一行字符,统计其中有多少个单词,单词之间用空格分隔开。 13.输入一个数,判断它是奇数还是偶数,如果…

Flink开发入门简单案例--统计实时流订单

Flink开发入门简单案例 0.简介1.订单数据生成器1.1 新建工程TestFlink1.2 在pom.xml中引入Flink依赖包1.3 订单数据生成类订单类(Item)订单生成数据流类测试订单生成类 2.订单统计2.1 仅统计订单中商品的件数 2.2 同时统计商品数量和金额 0.简介 本案例…

AI前景分析展望——GPTo1 SoraAI

引言 人工智能(AI)领域的飞速发展已不仅仅局限于学术研究,它已渗透到各个行业,影响着从生产制造到创意产业的方方面面。在这场技术革新的浪潮中,一些领先的AI模型,像Sora和OpenAI的O1,凭借其强大…

PAT1085 Perfect Sequence(25)

//判断是否是连续的数 //判断是否只能第一个数是最小值 #include <cstdio> #include <algorithm> typedef long long ll; using namespace std; int n,p; const int maxn 100010; int arr[maxn];int binary(int l, int r, ll tgt){if(arr[n-1] < tgt) return n…

QChart数据可视化

目录 一、QChart基本介绍 1.1 QChart基本概念与用途 1.2 主要类的介绍 1.2.1 QChartView类 1.2.2 QChart类 1.2.3QAbstractSeries类 1.2.4 QAbstractAxis类 1.2.5 QLegendMarker 二、与图表交互 1. 动态绘制数据 2. 深入数据 3. 缩放和滚动 4. 鼠标悬停 三、主题 …

SpringBoot源码-spring boot启动入口ruan方法主线分析(一)

一、SpringBoot启动的入口 1.当我们启动一个SpringBoot项目的时候&#xff0c;入口程序就是main方法&#xff0c;而在main方法中就执行了一个run方法。 SpringBootApplication public class StartApp {public static void main(String[] args) {// testSpringApplication.ru…

C#变量和函数如何和unity组件绑定

1.Button On_click (1)GameObject通过Add component添加上Script (2)Button选GameObject组件而不是直接选Script,直接选Script出现不了Script中的函数 2.RawImage 上面是错的 3.Text 上面是错的&#xff0c;应该是直接在GameObject里面填上对应的值 总结&#xff1a; …

Flink Sink的使用

经过一系列Transformation转换操作后&#xff0c;最后一定要调用Sink操作&#xff0c;才会形成一个完整的DataFlow拓扑。只有调用了Sink操作&#xff0c;才会产生最终的计算结果&#xff0c;这些数据可以写入到的文件、输出到指定的网络端口、消息中间件、外部的文件系统或者是…

【Spring MVC】如何获取cookie/session以及响应@RestController的理解,Header的设置

前言 &#x1f31f;&#x1f31f;本期讲解关于SpringMVC的编程之参数传递~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &#x1f386;那么废…

使用 exe4j 将 Spring Boot 项目打包为 EXE 可执行文件

使用 exe4j 将 Spring Boot 项目打包为 EXE 可执行文件 文章目录 使用 exe4j 将 Spring Boot 项目打包为 EXE 可执行文件什么是 exe4j准备工作打包 Spring Boot 项目为 EXE 文件1.启动 exe4j2. 选择项目类型3. 配置项目名称和输出目录4. 配置项目类型或可执行文件名称5. java配…

前端JavaScript(一)---基本介绍

Javascript是一种由Netscape(网景)的LiveScript发展而来的原型化继承的面向对象的动态类型的区分大小写的客户端脚本语言&#xff0c;主要目的是为了解决服务器端语言&#xff0c;比如Perl&#xff0c;遗留的速度问题&#xff0c;为客户提供更流畅的浏览效果。当时服务端需要对…

阿里Qwen系列开源模型介绍

模型种类丰富 Qwen2&#xff1a;包含Qwen2-0.5b、Qwen2-1.5b、Qwen2-7b、Qwen2-57b-a14b以及Qwen2-72b等五种规模的预训练和指令微调模型&#xff0c;其在多语言处理、长文本处理、代码生成、数学和逻辑推理等能力上&#xff0c;在mmlu、gpqa、humaneval等国际测评中得到了验证…

基于Java的小程序电商商城开源设计源码

近年来电商模式的发展越来越成熟&#xff0c;基于 Java 开发的小程序电商商城开源源码&#xff0c;为众多开发者和企业提供了构建个性化电商平台的有力工具。 基于Java的电子商城购物平台小程序的设计在手机上运行&#xff0c;可以实现管理员&#xff1b;首页、个人中心、用户…

开源 AI 智能名片 2 + 1 链动模式 S2B2C 商城小程序源码助力品牌共建:价值、策略与实践

摘要&#xff1a;在当今数字化商业环境下&#xff0c;品牌构建已演变为企业与消费者深度共建的过程。本文聚焦于“开源 AI 智能名片 2 1 链动模式 S2B2C 商城小程序源码”&#xff0c;探讨其如何融入品牌建设&#xff0c;通过剖析品牌价值构成&#xff0c;阐述该技术工具在助力…