精彩看点:为什么需要插入意向锁?
时间:2023-07-03 10:30:22来源:一树一溪

不久之前,有位读者问了一个关于insert intention waiting的问题,回答过程中,我还把意向锁(intention lock)和插入意向锁(insert intention lock)搞混了,实际上这是 2 种不同类型的锁。


(资料图片仅供参考)

为此,我研究了下插入意向锁,于是有了这篇文章。

本文基于 MySQL 8.0.32 源码,存储引擎为 InnoDB,事务隔离级别为可重复读。如需转载,请联系『一树一溪』公众号作者,转载后请标明来源。

正文

1、什么是插入意向锁?

我们先来看看官方定义:插入意向锁是由 INSERT 操作在插入记录之前加的一种间隙锁。

官方文档原文如下:

An insert intention lock  is a type of gap lock  set by INSERT operations  prior to row insertion.

我们再来看看插入意向锁的加锁代码:

// 为了方便阅读,代码格式做了调整。// storage/innobase/lock/lock0lock.ccdberr_t lock_rec_insert_check_and_lock(...){  ...  const ulint type_mode =               LOCK_X |               LOCK_GAP |               LOCK_INSERT_INTENTION;  const auto conflicting =      lock_rec_other_has_conflicting(type_mode, block, heap_no, trx);  ...  if (conflicting.wait_for != nullptr) {    RecLock rec_lock(thr, index, block, heap_no, type_mode);    trx_mutex_enter(trx);    err = rec_lock.add_to_waitq(conflicting.wait_for);    trx_mutex_exit(trx);  }  ...}

type_mode 包含了 3 个标志位:

LOCK_X,表示这是个排他锁。LOCK_GAP,表示这是个间隙锁。LOCK_INSERT_INTENTION,表示这是插入意向锁。

代码和官方文档可以相互印证:插入意向锁是一种排他(LOCK_X)间隙锁(LOCK_GAP)。

2、为什么需要插入意向锁?

通过前面的介绍,我们知道了:插入意向锁本质上是间隙锁。

那么,问题来了:既然有了间隙锁,那还弄个插入意向锁干啥?

答案当然是有用了。

有啥用?

说来话长。

那我们就长话长说,先从间隙锁说起。

我们先来看一下间隙锁的特点:

间隙锁的唯一用途是阻止其它事务插入记录到间隙中,以实现可重复读。共享间隙锁、排他间隙锁的功能完全一样。间隙锁可以共存,一个事务持有某个间隙的锁,该间隙锁释放之前,其它事务也可以申请并获得该间隙的锁,并且不区分共享锁还是排他锁。

由于多个间隙锁可以共存,插入记录需要加锁时,如果直接使用间隙锁,一个事务锁住了某个间隙,其它事务执行 INSERT 语句还可以插入记录到该间隙中,也就违背了间隙锁用于实现可重复读这一特点了。

为了解决这个问题,InnoDB 引入了插入意向锁。

上一小节,我们从lock_rec_insert_check_and_lock()代码看到了插入间隙锁的 type_mode:

const ulint type_mode =             LOCK_X |             LOCK_GAP |             LOCK_INSERT_INTENTION;

实际上,插入意向锁就是在排他间隙锁的基础上打了个LOCK_INSERT_INTENTION标志。

我们通过具体的应用场景来看一下LOCK_INSERT_INTENTION标志的作用机制:

图片

事务 T 执行 INSERT 语句,插入记录 R到某个表的记录 R1之前。

如果其它事务对 R1 前面的间隙加了(共享或排他)间隙锁,事务 T 会申请对该间隙加插入意向锁。

因为插入意向锁有LOCK_INSERT_INTENTION标志,识别到这个标志,InnoDB 就会让 INSERT 语句进入等待状态。

直到 R1 前面间隙的锁被释放,INSERT 语句才能获得插入意向锁,插入记录 R 到 R1 前面的间隙中。

通过LOCK_INSERT_INTENTION标志的介绍可以看到,插入记录时,只有使用插入意向锁,其它事务持有的间隙锁才能阻止插入操作插入记录到间隙中。

也就是说,间隙锁需要插入意向锁的配合,才能实现可重复读,这就是为什么需要插入意向锁的原因了。

3、插入意向锁和其它锁的关系

为了介绍这一小节的内容,我们需要先做点准备工作。

创建测试表:

USE `test`;CREATE TABLE `t1` (  `id` int unsigned NOT NULL AUTO_INCREMENT,  `i1` int DEFAULT "0",  PRIMARY KEY (`id`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

插入测试数据:

INSERT INTO `t1`(`id`, `i1`)VALUES (10, 101), (20, 201), (30, 301);

数据如下:

通过以下命令创建 3 个数据库连接备用:

mysql -h127.0.0.1 -uroot -D test

3 个连接分别命名为session 1、session 2、session 3。

(1)间隙锁会阻塞插入意向锁

我们可以按以下步骤,验证间隙锁会阻塞插入意向锁。

第 1 步,在session 1中执行以下 SQL,对id = 20的记录加间隙锁:

BEGIN;SELECT * FROM `t1`WHERE `id` > 10 AND `id` < 20FOR SHARE;

SELECT 语句会锁住id = 20的记录前面的间隙。

第 2 步,在session 2中执行以下 SQL,插入一条记录到id = 20的记录之前:

BEGIN;INSERT INTO `t1`(`id`, `i1`)VALUES (12, 121);

INSERT 语句发现id = 20的记录前面的间隙被锁住了,会申请对该间隙加插入意向锁,并进入等待状态。

第 3 步,在session 3中执行以下 SQL,查看加锁情况:

SELECT  `engine_transaction_id` as `trx_id`,  `object_name` as `table`,  `index_name` as `index`,  `lock_type`, `lock_mode`,  `lock_status`, `lock_data`FROM `performance_schema`.`data_locks`WHERE `lock_type` = "RECORD"AND object_schema = "test";

图片

通过加锁情况,我们可以确认间隙锁会阻塞插入意向锁:

session 1的 SELECT 语句持有间隙锁(lock_mode 包含GAP)。session 2的 INSERT 语句正在等待插入意向锁(lock_mode 包含INSERT_INTENTION)。

最后,在session 1、session 2中执行ROLLBACK语句,回滚事务,为后面的验证工作做准备。

(2)插入意向锁不会阻塞间隙锁

我们可以按以下步骤,验证插入意向锁不会阻塞间隙锁。

第 1 步,在session 1中执行以下 SQL,对id = 20的记录加间隙锁:

BEGIN;SELECT * FROM `t1`WHERE `id` > 10 AND `id` < 20FOR SHARE;

SELECT 语句会锁住id = 20的记录前面的间隙。

第 1 步加间隙锁,是为了引发第 2 步的 INSERT 语句加插入意向锁。

第 2 步,在session 2中执行以下 SQL,插入一条记录到id = 20的记录之前:

BEGIN;INSERT INTO `t1`(`id`, `i1`)VALUES (12, 121);

INSERT 语句发现id = 20的记录前面的间隙被锁住了,会申请对该间隙加插入意向锁,并进入等待状态。

第 3 步,在session 1中执行以下 SQL,回滚事务:

ROLLBACK;

SELECT 语句释放间隙锁之后,第 2 步session 2中的 INSERT 语句成功获得插入意向锁。

第 4 步,在session 1中执行以下 SQL,对id = 20的记录加间隙锁:

BEGIN;SELECT * FROM `t1`-- `id` > xx 中的 xx 取值为 15WHERE `id` > 15 AND `id` < 20FOR SHARE;

注意:因为第 2 步的 INSERT 语句在id = 10 ~ 20之间插入了id = 12的记录,第 4 步WHERE 条件id > xx中的xx必须大于12,否则会触发id = 12的记录上的隐式锁逻辑,导致 SELECT 语句等待id = 20的记录上的next-key锁。

第 5 步,在session 3中执行以下 SQL,查看加锁情况:

SELECT  `engine_transaction_id` as `trx_id`,  `object_name` as `table`,  `index_name` as `index`,  `lock_type`, `lock_mode`,  `lock_status`, `lock_data`FROM `performance_schema`.`data_locks`WHERE `lock_type` = "RECORD"AND object_schema = "test";

通过加锁情况,我们可以确认插入意向锁不会阻塞间隙锁:

session 2中,第 2 步的 INSERT 语句持有插入意向锁(lock_mode 包含INSERT_INTENTION)。session 1中,第 4 步的 SELECT 语句成功获得了间隙锁(lock_mode 包含GAP)。

最后,在session 1、session 2中执行ROLLBACK语句,回滚事务,为后面的验证工作做准备。

(3)插入意向锁相互之间不会阻塞

我们可以按以下步骤,验证插入意向锁相互之间不会阻塞。

第 1 步,在session 1中执行以下 SQL,对id = 20的记录加间隙锁:

BEGIN;SELECT * FROM `t1`WHERE `id` > 10 AND `id` < 20FOR SHARE;

SELECT 语句会锁住id = 20的记录前面的间隙。

执行这一步是为了让第 2、3 步的 INSERT 语句都申请对id = 20的记录前面的间隙加插入意向锁,并进入等待状态。

第 2 步,在session 2中执行以下 SQL,插入一条记录到id = 20的记录之前:

BEGIN;INSERT INTO `t1`(`id`, `i1`)VALUES (12, 121);

INSERT 语句发现id = 20的记录前面的间隙被锁住了,会申请对该间隙加插入意向锁,并进入等待状态。

第 3 步,在session 3中执行以下 SQL,插入一条记录到id = 20的记录之前:

BEGIN;INSERT INTO `t1`(`id`, `i1`)VALUES (15, 151);

INSERT 语句发现id = 20的记录前面的间隙被锁住了,会申请对该间隙加插入意向锁,并进入等待状态。

第 4 步,在session 1中执行以下 SQL,查看锁等待情况:

SELECT  `engine_transaction_id` as `trx_id`,  `object_name` as `table`,  `index_name` as `index`,  `lock_type`, `lock_mode`,  `lock_status`, `lock_data`FROM `performance_schema`.`data_locks`WHERE `lock_type` = "RECORD"AND object_schema = "test";

图片

由于id = 20的记录前面的间隙被第 1 步的 SELECT 语句锁住了,第 2、3 步的 INSERT 语句正在等待该间隙的插入意向锁。

第 5 步,在session 1中执行回滚语句,释放id = 20的记录上的间隙锁:

ROLLBACK;

第 6 步,在session 1中执行以下 SQL,查看加锁情况:

SELECT  `engine_transaction_id` as `trx_id`,  `object_name` as `table`,  `index_name` as `index`,  `lock_type`, `lock_mode`,  `lock_status`, `lock_data`FROM `performance_schema`.`data_locks`WHERE `lock_type` = "RECORD"AND object_schema = "test";

第 2、3 步的 INSERT 语句同时获得了id = 20的记录前面间隙的插入意向锁。

通过加锁情况,我们可以确认插入意向锁相互之间不会阻塞。

3.4 next-key 锁和插入意向锁会相互阻塞吗?

对于 next-key 锁和插入意向锁是否会相互阻塞,这里只给出结论:

next-key 锁会阻塞插入意向锁。插入意向锁不会阻塞 next-key 锁。

感兴趣的读者可以按照 3.1、3.2 小节的步骤自行测试,毕竟自己动手获得的知识才会记得更牢。

测试时,需要把 SELECT 语句 WHERE 条件中的id < 20替换为id <= 20,确保 SELECT 语句加的是 next-key 锁而不是普通的间隙锁。

4. 怎么知道加了插入意向锁?

我们通过查询performance_schema.data_locks,可以知道某个事务是否申请了对某个间隙加插入间隙锁,这种方式我们在上一小节中已经使用过多次。

如果查询结果中某条记录的 lock_mode 字段包含INSERT_INTENTION,说明对应的事务申请了加插入意向锁。

lock_status =WAITING说明正在等待插入意向锁。

lock_status =GRANTED说明已经获得了插入意向锁。

还有一种方式,只能看到正在等待的插入意向锁,无法看到已经获得的插入意向锁。

执行SHOW ENGINE InnoDB STATUS语句,部分结果如下:

-- 为了方便阅读,对以下结果的格式做了调整-- TRX HAS BEEN WAITING 2 SEC FOR THIS LOCK TO BE GRANTED:RECORD LOCKS space id 0 page no 47       n bits 72       index PRIMARY of table `test`.`t1`       trx id 133955       lock_mode X       locks gap before rec       insert intention waiting

通过以上结果,我们可以得到以下信息:

事务133955正在等待(waiting)获取插入意向锁(insert intention):

lock_mode X对应 type_mode 中的LOCK_X。locks gap before rec对应 type_mode 中的LOCK_GAP。insert intention对应 type_mode 中的LOCK_INSERT_INTENTION。5、总结

在排他(LOCK_X)间隙锁(LOCK_GAP)的基础上增加LOCK_INSERT_INTENTION标志,就得到了插入意向锁,所以,从本质上来说,插入意向锁是个特殊的间隙锁。

间隙锁需要插入意向锁的配合,才能阻塞其它事务插入记录到某个间隙中,从而实现可重复读,这就是需要插入意向锁的原因了。

标签:

最新
  • 精彩看点:为什么需要插入意向锁?

    不久之前,有位读者问了一个关于insertintentionwaiting的问题,回答过

  • 腾讯电脑管家好用吗(电脑管家和360哪个好)|每日快看

    答:这个问题的答案是肯定的。腾讯电脑管家软件是国内知名的电脑防护软

  • 千亿规模的垂直农业,中国农业现代化的新赛道?

    千亿规模的垂直农业,中国农业现代化的新赛道?,相传在公元前600年,古

  • 2022春晚进行时辽视春晚现场直播_2022辽视春晚有看头 焦点热闻

    hello大家好,我是大学网网小航来为大家解答以上问题,2022春晚进行时

  • 全球快播:百克生物:正推进重组带状疱疹疫苗项目,目前正处于临床前研究阶段

    【百克生物:正推进重组带状疱疹疫苗项目,目前正处于临床前研究阶段】

  • 聚焦:威马汽车、天际汽车再传“利空” 造车新势力“掉队者”可能撑不过今年?

    本报记者 龚梦泽距离宣布全力开展复工复产仅三个月,威马汽车再传

  • 中国中免遭沪股通连续7日净卖出 合计净卖出15.93亿元

    6月29日中国中免遭沪股通净卖出6 19亿元,为连续7日净卖出,合计净卖出

  • 信阳中院召开全市法院司法行装工作会议

    6月28日,信阳市中级人民法院召开2023年全市法院司法行装工作会议,信

  • 资讯推荐:我国财产保险属于什么?财产保险怎么定义?

    财产保险属于什么:一,财产保险合同是属于诺成合同。财产保险合同

  • 生化危机5如何联机_联机的方法_环球速讯

    hello大家好,我是城乡经济网小晟来为大家解答以上问题,生化危机5如何

  • 世界看点:剪映电脑版怎么剪辑视频? 剪映怎么剪辑音乐长短?

    剪映电脑版怎么剪辑视频?1、首先我们打开软件,导入一段视频。2、素

  • 全球时讯:2023年常州高中毕业师范生培养说明+电话

    一、常州高中毕业生师范生培养有关说明1、培养对象按《专业培养与择优

  • 快讯:南山荔枝文化旅游节举行“潮·青春”时装艺术展演

    深圳新闻网2023年6月27日讯(深圳特区报记者王志明)新潮的设计、新颖

  • 观点:法院敲诈勒索2500怎么判?

    证据有七种:(一)物证、书证;(二)证人证言;(三)被害人陈述;(

  • 【环球播资讯】仅仅播2集,收视率破1.5,不愧是你们盼了两年的职场大剧

    文案| 十四北编辑| 南枫白玉兰大奖落幕,雷佳音凭《人世间》实至名归

  • ORIGIN PC推出EON16-SL笔记本:i9-13900H、RTX 4070,1939美元起

    IT之家6月26日消息,ORIGINPC推出新款EON16-SL笔记本电脑,新款笔记本

  • 旅游
    • AI+时代:谁将赢得先机?_世界今日讯

    • 每日快播:7月4日美国国庆日_7月4日 美国

    • 天天最新:三星铁粉管泽元:Gen.G状态真不错,但这DK教练也很抽象啊

    • 环球通讯!内蒙古11选五开奖结果一定牛 内蒙古11选五开奖结果