我的编程空间,编程开发者的网络收藏夹
学习永远不晚

详细剖析MySQL临键锁

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

详细剖析MySQL临键锁

💗推荐阅读文章💗

🎉本博客知识点收录于🎉👉🚀《MySQL系列教程》🚀—>✈️03【锁、事务原理、MVCC】✈️

文章目录

2.4 临键锁

2.4.1 临键锁的区间测试

临键锁(Next-Key Lock):临键锁是查询时InnoDB根据查询的条件而锁定的一个范围,这个范围中包含有间隙锁和记录数;临键锁=间隙锁+记录锁
其设计的目的是为了解决Phantom Problem(幻读);主要是阻塞insert,但由于临键锁中包含有记录锁,因此临键锁所锁定的范围内如果包含有记录,那么也会给这些记录添加记录锁,从而造成阻塞除insert之外的操作;
Tips:临键锁的主要目的,也是为了避免幻读(Phantom Read)。如果把事务的隔离级别降级为RC,临键锁则也会失效。
临键锁锁住的区间为:记录+区间(左开右闭)
左开右闭:不锁住左边,锁右边
测试表:

drop table if exists t2;CREATE TABLE `t2`  (  `id` int(11) NOT NULL AUTO_INCREMENT,  `num` int(11) ,  PRIMARY KEY (`id`) USING BTREE) ENGINE = InnoDB ;INSERT INTO `t2`(`id`, `num`) VALUES (5, 5);INSERT INTO `t2`(`id`, `num`) VALUES (10, 10);INSERT INTO `t2`(`id`, `num`) VALUES (15, 15);INSERT INTO `t2`(`id`, `num`) VALUES (20, 20);-- 创建普通索引create index idx_num on t2(num);-- 创建唯一索引create unique index idx_num on t2(num);-- 删除索引drop index idx_num on t2;
  • 区间示意图:


Tips:间隙锁只会阻塞insert,记录锁会阻塞任意的锁(单要注意排他锁和共享锁的关系);
【测试案例-01-间隙锁】
临键锁的触发不仅把条件区间(11-16)的数据行锁住了,还把临键的数据行统统锁住了;锁住的区间为:(10,15]、(15,20]
锁住的id范围:10(不含)~20(含)

session1session2
begin;
begin;
select * from t2 where id>11 and id<16 for update;
insert into t2 values(10,0); – 不阻塞
insert into t2 values(11,0); – 阻塞
insert into t2 values(15,0); – 阻塞
insert into t2 values(16,0); – 阻塞
insert into t2 values(18,10); – 阻塞
insert into t2 values(20,0); – 阻塞
insert into t2 values(21,0); – 不阻塞
rollback;
rollback;

【案例测试-02-记录锁】
临键锁是间隙锁+记录数的;上述案例中测试了临键锁中的间隙锁,这次我们来测试一下临键锁中的记录锁;

session1session2
begin;
begin;
select * from t2 where id>11 and id<16 for update;
select * from t2 where id=12 for update; – 间隙锁,不阻塞
select * from t2 where id=15 for update; – 记录数,阻塞
select * from t2 where id=17 for update; – 间隙锁,不阻塞
select * from t2 where id=20 for update; – 记录数,不阻塞
rollback;
rollback;

2.4.2 临键锁-普通列

我们刚刚测试的是以主键索引进行测试,如果采用不同的列(普通列、普通索引、唯一索引/主键索引等),则临键锁中的间隙锁和记录锁住的内容大不相同;
如果查询的是普通列,那么触发的临键锁为:表级别的间隙锁+表级别的记录锁

  • 测试表:
drop table if exists t2;CREATE TABLE `t2`  (  `id` int(11) NOT NULL AUTO_INCREMENT,  `num` int(11) ,  PRIMARY KEY (`id`) USING BTREE) ENGINE = InnoDB ;INSERT INTO `t2`(`id`, `num`) VALUES (5, 5);INSERT INTO `t2`(`id`, `num`) VALUES (10, 10);INSERT INTO `t2`(`id`, `num`) VALUES (15, 15);INSERT INTO `t2`(`id`, `num`) VALUES (20, 20);
1)间隙锁

【案例测试-01-表级别间隙锁】

session1session2
begin;
begin;
select * from t2 where num=11 for update;
insert into t2 values(null,3); – 阻塞
insert into t2 values(null,5); – 阻塞
insert into t2 values(null,8); – 阻塞
insert into t2 values(null,10); – 阻塞
insert into t2 values(null,18); – 阻塞
insert into t2 values(null,21); – 阻塞
rollback;
rollback;

Tips:innoDB查询如果没有使用到索引默认触发表级临键锁,把所有的间隙都锁住了

2)记录锁

以普通列查询除了会触发表级别的临键锁外,同时还会触发表级别的记录锁;
【案例测试-02-表级别记录锁】

session1session2
begin;
begin;
select * from t2 where num=11 for update;
select * from t2 where id=3 for update; – 间隙锁,不阻塞
select * from t2 where id=5 for update; – 记录数,阻塞
select * from t2 where id=8 for update; – 间隙锁,不阻塞
select * from t2 where id=15 for update; – 记录数,阻塞
select * from t2 where id=18 for update; – 间隙锁,不阻塞
select * from t2 where id=20 for update; – 记录数,阻塞
rollback;
rollback;

2.4.3 临键锁-普通索引

如果查询的列为普通索引列,要看被查询的记录是否在临界值,以及是否是范围查询,才能判断临建锁的范围;

  • 被查询的记录是否在临界值情况:
    • 非临界值:那么间隙锁为当前记录所在的区间,记录锁则不会生效(记录锁不存在);
    • 临界值:那么间隙锁为相邻的两个区间,记录锁退化成行锁(即只会锁住被查询的那条记录);
  • 范围查询情况:间隙锁为范围所涉及到的所有区间,记录锁也会升级为范围锁涉及到的区间
drop table if exists t2;CREATE TABLE `t2`  (  `id` int(11) NOT NULL AUTO_INCREMENT,  `num` int(11) ,  PRIMARY KEY (`id`) USING BTREE) ENGINE = InnoDB ;INSERT INTO `t2`(`id`, `num`) VALUES (5, 5);INSERT INTO `t2`(`id`, `num`) VALUES (10, 10);INSERT INTO `t2`(`id`, `num`) VALUES (15, 15);INSERT INTO `t2`(`id`, `num`) VALUES (20, 20);-- 创建普通索引create index idx_num on t2(num);
1)非临界值

当使用普通索引列查询,查询的记录不处于临界值时,那么间隙锁为被查询记录所在的区间,记录锁则不会生效;
【测试案例-01-间隙锁】

session1session2
begin;
begin;
– 触发间隙锁,锁住(15,20]区间
select * from t2 where num=17 for update;
insert into t2 values(null,15); – 阻塞
insert into t2 values(null,18); – 阻塞
insert into t2 values(null,20); – 不阻塞
rollback;
rollback;

num=17这条记录不是会锁定(15,20]区间吗?为什么15被阻塞了,20反而没被阻塞呢?
这里需要牵扯到另一个问题了,在InnoDB中,相同的普通索引的叶子节点是以主键的顺序进行排列的,我们来模拟一下刚刚插入的数据在B+Tree上的变化:

只考虑叶子节点的变化,可以看到在上图在演变的过程中产生了分裂情况(假设每个叶子节点都只存储两个元素),如果普通索引的重复值太多势必会造成大量的分裂情况,减低插入效率,因此索引列不宜选择重复率太大的列;
再看下图数据库表中实际存储的列的样子我们就会明白为什么num=20不阻塞,num=15阻塞了

  • num索引列排列情况:


查询示意图:

【测试案例-02-间隙锁】
当我们把id列的影响也计算进来时,数据就符合我们正常分析的情况了:

session1session2
begin;
begin;
– 触发间隙锁,锁住(15,20]区间
select * from t2 where num=17 for update;
insert into t2 values(14,15); – 不阻塞
insert into t2 values(18,18); – 阻塞
insert into t2 values(19,20); – 阻塞
rollback;
rollback;

【测试案例-03-记录锁】
当使用普通索引列查询,查询的记录不处于临界值时,那么间隙锁为被查询记录所在的区间,记录锁则不会生效

session1session2
begin;
begin;
– 没有num=17的这条记录,记录锁不会存在
select * from t2 where num=17 for update;
select * from t2 where num=15 for update; – 不阻塞
select * from t2 where num=16 for update; – 不阻塞
select * from t2 where num=17 for update; – 不阻塞
select * from t2 where num=20 for update; – 不阻塞
rollback;
rollback;
2)临界值

【测试案例-01-间隙锁】
当使用普通索引列来查询,并且查询的记录处于临界值时,那么间隙锁为相邻的两个区间,记录锁退化成行锁;
下面案例将会锁住(10,15]、(15,20]两个区间

session1session2
begin;
begin;
– 触发的间隙锁的区间为(10,15]、(15,20]
select * from t2 where num=15 for update;
insert into t2 values(null,8); – 不阻塞
insert into t2 values(null,10); – 阻塞
insert into t2 values(null,11); – 阻塞
insert into t2 values(null,15); – 阻塞
insert into t2 values(null,18); – 阻塞
insert into t2 values(null,20); – 不阻塞
rollback;
rollback;

发现实际插入的数据跟我们分析的情况不一致,这个时候我们依然也要观察B+Tree的实现:

  • 索引底层构建过程:

  • 临键锁区间:


15处于(10,15]和(15,20]两个临键区间,因此在两个区间内的数据行都被锁住了
【测试案例-02-记录锁】
当使用普通索引列来查询,并且查询的记录处于临界值时,那么间隙锁为相邻的两个区间,记录锁退化成行锁

session1session2
begin;
begin;
– 记录锁只锁住num=15这行记录
select * from t2 where num=15 for update;
select * from t2 where num=10 for update; – 不阻塞
select * from t2 where num=12 for update; – 不阻塞
select * from t2 where num=15 for update; – 阻塞
select * from t2 where num=18 for update; – 不阻塞
select * from t2 where num=20 for update; – 不阻塞
select * from t2 where num=22 for update; – 不阻塞
rollback;
rollback;
3)范围值

【测试案例-01-间隙锁】
当使用普通索引进行条件范围查询时,那么间隙锁查询范围所涉及到的区间,记录锁也会升级为查询范围涉及到的区间;

session1session2
begin;
begin;
– 间隙锁为(10,20]区间
select * from t2 where num>11 and num <16 for update;
insert into t2 values(9,10); – 不阻塞
insert into t2 values(11,10); – 阻塞(参考B+Tree的构建)
insert into t2 values(11,11); – 阻塞
insert into t2 values(12,12); – 阻塞
insert into t2 values(15,15); – 阻塞(被记录锁阻塞)
insert into t2 values(18,18); – 阻塞
insert into t2 values(19,20); – 阻塞
insert into t2 values(21,20); – 不阻塞(参考B+Tree的构建)
rollback;
rollback;
  • 分析底层B+Tree构建情况:


【测试案例-02-记录锁】
当使用普通索引进行条件范围查询时,那么间隙锁查询范围所涉及到的区间,记录锁也会升级为查询范围涉及到的区间;

session1session2
begin;
begin;
– 记录锁的区间为(10,20]区间
select * from t2 where num>11 and num <16 for update;
select * from t2 where num=10 for update; – 不阻塞(左开右闭)
select * from t2 where num=12 for update; – 不阻塞(属于间隙)
select * from t2 where num=15 for update; – 阻塞(触发记录锁)
select * from t2 where num=16 for update; – 不阻塞(属于间隙)
select * from t2 where num=18 for update; – 不阻塞(属于间隙)
select * from t2 where num=20 for update; – 阻塞(左开右闭,触发记录锁)
select * from t2 where num=21 for update; – 不阻塞(即是间隙,也不在区间)
rollback;
rollback;

2.4.4 临键锁-主键和唯一索引

如果查询的是唯一索引或主键索引,也要看被查询的记录是否在临界值;是否是范围查询等

  • 被查询的记录是否在临界值情况:
    • 不在临界值:间隙锁为当前被查询的记录所在的区间,记录锁会消失;
    • 在临界值:间隙锁会消失,记录锁退化成行锁
  • 范围查询情况:间隙锁为范围查询所涉及到的所有区间,记录数也会升级为范围所涉及到的区间(和普通索引的效果一致);

创建唯一索引:

-- 删除索引drop index idx_num on t2;-- 创建唯一索引create unique index idx_num on t2(num);
1)非临界值

唯一索引在查询非临界值的记录时和普通索引的特点一样,即==间隙锁为当前记录所在的区间,记录锁不生效;==
【测试案例-01-间隙锁】

session-01session-02
begin;
begin;
– 间隙锁锁住的区间为(15,20]
select * from t2 where num=17 for update;
insert into t2 values(null,11); – 不阻塞
insert into t2 values(null,15); – 不阻塞
insert into t2 values(null,16); – 阻塞
insert into t2 values(null,18); – 阻塞
insert into t2 values(null,20); – 不阻塞
insert into t2 values(null,21); – 不阻塞
  • 分析num列索引的B+Tree底层构建情况:


Tips:唯一索引冲突时MySQL会立即响应,不会触发临键锁
【测试案例-02-记录锁】
唯一索引在查询非临界值的记录时,记录锁不生效;

session1session2
begin;
begin;
– 没有num=17的这条记录,记录锁不会存在
select * from t2 where num=17 for update;
select * from t2 where num=15 for update; – 不阻塞
select * from t2 where num=16 for update; – 不阻塞
select * from t2 where num=17 for update; – 不阻塞
select * from t2 where num=20 for update; – 不阻塞
rollback;
rollback;
2)临界值

在使用唯一索引查询临界值时,间隙锁会消失,记录锁会退化成行锁;
【测试案例-01-间隙锁】

session1session2
begin;
begin;
select * from t2 where num=15 for update;
insert into t2 values(null,4); – 不阻塞
insert into t2 values(null,8); – 不阻塞
insert into t2 values(null,11); – 不阻塞
insert into t2 values(null,15); – 阻塞(阻塞的原因是记录锁,而不是间隙锁)
insert into t2 values(null,28); – 不阻塞
rollback;insert into t2 values(null,20); – 不阻塞
rollback;

【测试案例-02-记录锁】

session1session2
begin;
begin;
– 记录锁只锁住num=15这行记录
select * from t2 where num=15 for update;
select * from t2 where num=10 for update; – 不阻塞
select * from t2 where num=12 for update; – 不阻塞
select * from t2 where num=15 for update; – 阻塞
select * from t2 where num=18 for update; – 不阻塞
select * from t2 where num=20 for update; – 不阻塞
select * from t2 where num=22 for update; – 不阻塞
rollback;
rollback;
3)范围值

【测试案例-01-间隙锁】
当使用普通索引进行条件范围查询时,那么间隙锁查询范围所涉及到的区间,记录锁也会升级为查询范围涉及到的区间;

session1session2
begin;
begin;
– 间隙锁为(10,20]区间
select * from t2 where num>11 and num <16 for update;
insert into t2 values(9,10); – 不阻塞
insert into t2 values(11,10); – 阻塞(参考B+Tree的构建)
insert into t2 values(11,11); – 阻塞
insert into t2 values(12,12); – 阻塞
insert into t2 values(15,15); – 阻塞(被记录锁阻塞)
insert into t2 values(18,18); – 阻塞
insert into t2 values(19,20); – 阻塞
insert into t2 values(21,20); – 不阻塞(参考B+Tree的构建)
rollback;
rollback;
  • 分析底层B+Tree构建情况:


【测试案例-02-记录锁】
当使用普通索引进行条件范围查询时,那么间隙锁查询范围所涉及到的区间,记录锁也会升级为查询范围涉及到的区间;

session1session2
begin;
begin;
– 记录锁的区间为(10,20]区间
select * from t2 where num>11 and num <16 for update;
select * from t2 where num=10 for update; – 不阻塞(左开右闭)
select * from t2 where num=12 for update; – 不阻塞(属于间隙)
select * from t2 where num=15 for update; – 阻塞(触发记录锁)
select * from t2 where num=16 for update; – 不阻塞(属于间隙)
select * from t2 where num=18 for update; – 不阻塞(属于间隙)
select * from t2 where num=20 for update; – 阻塞(左开右闭,触发记录锁)
select * from t2 where num=21 for update; – 不阻塞(即是间隙,也不在区间)
rollback;
rollback;

2.4.5 临键锁总结

临键锁是InnoDB在查询数据时锁定的一个范围,这个范围包含有间隙锁和记录锁;根据查询的条件不同、列的类型不同(是否是索引等)触发的临键锁范围也不同;

  • 普通列:临键锁中的间隙锁和记录数均为表级别;
  • 普通索引列:
    • 非临界值:间隙锁为被查询的记录所在的区间,记录锁不生效
    • 临界值:间隙锁为被查询记录所在的相邻两个区间,记录数退化为行锁
    • 范围值:间隙锁和记录数均为查询条件所涉及到的区间
  • 唯一索引或主键索引列:
    • 非临界值:间隙锁为被查询的记录所在的区间,记录锁不生效
    • 临界值:间隙锁失效,记录锁退化为行锁
    • 范围值:间隙锁和记录数均为查询条件所涉及到的区间

Tips:临键锁的主要目的,也是为了避免幻读(Phantom Read)。如果把事务的隔离级别降级为RC,临键锁则也会失效。

来源地址:https://blog.csdn.net/Bb15070047748/article/details/131766686

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

详细剖析MySQL临键锁

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

什么是mysql临键锁

本篇内容主要讲解“什么是mysql临键锁”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“什么是mysql临键锁”吧!说明1、作用于非唯一索引上,是记录锁与间隙锁的组合。2、左开右闭区间,它锁定的范
2023-06-20

MySQL的锁机制——记录锁、间隙锁、临键锁

记录锁(Record Locks) 记录锁锁住的是索引记录,记录锁也叫行锁。如果使用索引作为条件命中了记录,那么就是记录锁,被锁住的记录不能被别的事务插入相同的索引键值,修改和删除。 例如: select * from test_table
2023-08-18

MySQL间隙锁和临键锁的区别有哪些

MySQL中的间隙锁(Gap Lock)和临键锁(Next-Key Lock)是用于并发控制的锁机制,用于处理多个事务同时操作同一数据范围的情况。它们的区别如下:锁的范围:间隙锁是在索引范围之间的间隙上进行锁定,而临键锁是在索引记录上进行锁
2023-10-21

详细分析mysql MDL元数据锁

前言: 当你在MySQL中执行一条SQL时,语句并没有在你预期的时间内执行完成,这时候我们通常会登陆到MySQL数据库上查看是不是出了什么问题,通常会使用的一个命令就是 show processlist,看看有哪些session,这些ses
2022-05-23

【Mysql系列】——详细剖析数据库“索引”【上篇】

【Mysql系列】——详细剖析数据库中的核心知识【索引】😎 前言🙌索引索引概述为什么需要索引?索引的优缺点索引结构索引的结构为什么不是二叉树和红黑树?索引的B+树结构索引的Hash结构Hash结构索引
2023-08-16

MySQL死锁问题排查与详细分析

目录前言1. 死锁的基本概念1.1 死锁的定义1.2 死锁的四个必要条件2. 死锁的常见原因2.1 事务并发控制不当2.2 事务顺序不一致2.3 资源竞争激烈2.4 事务设计不合理3. 死锁的排查方法3.1 查看死锁日志3.1.1 启用死锁
MySQL死锁问题排查与详细分析
2024-09-11

JavaSynchronized的偏向锁详细分析

synchronized作为Java程序员最常用同步工具,很多人却对它的用法和实现原理一知半解,以至于还有不少人认为synchronized是重量级锁,性能较差,尽量少用。但不可否认的是synchronized依然是并发首选工具,本文就来详细讲讲
2023-05-15

Redis超详细分析分布式锁

目录分布式锁应用场景使用Redis 实现分布式锁单机版Redis实现分布式锁使用原生Jedis实现使用Springboot实现分布式锁为了保证一个方法在高并发情况下的同一时间只能被同一个线程执行,在传统单体应用单机部署的情况下,可以使用J
2022-07-27

AndroidLock锁实现原理详细分析

这篇文章主要介绍了AndroidLock锁实现原理,Lock接口的实现类提供了比使用synchronized关键字更加灵活和广泛的锁定对象操作,而且是以面向对象的方式进行对象加锁
2023-02-17

C++ Explicit关键字详细解析

以下是对C++中Explicit关键字的用法进行了详细的介绍,需要的朋友可以过来参考下,希望对大家有所帮助
2022-11-15

C++临时性对象的生命周期详细解析

临时性对象的被摧毁,应该是对完整表达式(full-expression)求值过程中的最后一个步骤。该完整表达式造成临时对象的产生
2022-11-15

剖析6个MySQL死锁案例的原因以及死锁预防策略

MySQL 死锁是面试常问问题,金三银四,所以最近面试相关的文章比较多,本文章是总结的一波死锁问题,和大家分享一下。 Mysql 锁类型和加锁分析 MySQL有三种锁的级别:页级、表级、行级。 **表级锁:**开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲
剖析6个MySQL死锁案例的原因以及死锁预防策略
2015-02-16

编程热搜

目录