浅谈MySQL next-key lock 加锁范围


Posted in MySQL onJune 07, 2021

前言

某天,突然被问到 MySQL 的 next-key lock,我瞬间的反应就是:

浅谈MySQL next-key lock 加锁范围

这都是啥啥啥???

浅谈MySQL next-key lock 加锁范围

这一个截图我啥也看不出来呀?

仔细一看,好像似曾相识,这不是《MySQL 45 讲》里面的内容么?

什么是 next-key lock

A next-key lock is a combination of a record lock on the index record and a gap lock on the gap before the index record.

官网的解释大概意思就是:next-key 锁是索引记录上的记录锁和索引记录之前的间隙上的间隙锁的组合。

先给自己来一串小问号???

  • 在主键、唯一索引、普通索引以及普通字段上加锁,是锁住了哪些索引?
  • 不同的查询条件,分别锁住了哪些范围的数据?
  • for share 和 for update 等值查询和范围查询的锁范围?
  • 当查询的等值不存在时,锁范围是什么?
  • 当查询条件分别是主键、唯一索引、普通索引时有什么区别?

浅谈MySQL next-key lock 加锁范围

既然啥都不懂,那只好从头开始操作实践一把了!

先看看看 《MySQL 45 讲》中丁奇老师的结论:

浅谈MySQL next-key lock 加锁范围

看了这结论,应该可以解答一大部分问题,不过有一句非常非常重点的话需要关注:MySQL 后面的版本可能会改变加锁策略,所以这个规则只限于截止到现在的最新版本,即 5.x 系列<=5.7.24,8.0 系列 <=8.0.13

所以,以上的规则,对现在的版本并不一定适用,下面我以 MySQL 8.0.25 版本为例,进行多角度验证 next-key lock 加锁范围。

环境准备

MySQL 版本:8.0.25

隔离级别:可重复读(RR)

存储引擎:InnoDB

mysql> select @@global.transaction_isolation,@@transaction_isolation\G
mysql> show create table t\G

浅谈MySQL next-key lock 加锁范围

如何使用 Docker 安装 MySQL,可以参考另一篇文章《使用 Docker 安装并连接 MySQL》

主键索引

首先来验证主键索引的 next-key lock 的范围

浅谈MySQL next-key lock 加锁范围

此时数据库的数据如图所示,对主键索引来说此时数据间隙如下:

浅谈MySQL next-key lock 加锁范围

主键等值查询 —— 数据存在

mysql> begin; select * from t where id = 10 for update;

这条 SQL,对 id = 10 进行加锁,可以先思考一下加了什么锁?锁住了什么数据?

可以通过 data_locks 查看锁信息,SQL 如下:

# mysql> select * from performance_schema.data_locks;
mysql> select * from performance_schema.data_locks\G

具体字段含义可以参考 官方文档

浅谈MySQL next-key lock 加锁范围

结果主要包含引擎、库、表等信息,咱们需要重点关注以下几个字段:

  • INDEX_NAME:锁定索引的名称
  • LOCK_TYPE:锁的类型,对于 InnoDB,允许的值为 RECORD 行级锁 和 TABLE 表级锁。
  • LOCK_MODE:锁的类型:S, X, IS, IX, and gap locks
  • LOCK_DATA:锁关联的数据,对于 InnoDB,当 LOCK_TYPE 是 RECORD(行锁),则显示值。当锁在主键索引上时,则值是锁定记录的主键值。当锁是在辅助索引上时,则显示辅助索引的值,并附加上主键值。

结果很明显,这里是对表添加了一个 IX 锁 并对主键索引 id = 10 的记录,添加了一个 X,REC_NOT_GAP 锁,表示只锁定了记录。

同样 for share 是对表添加了一个 IS 锁并对主键索引 id = 10 的记录,添加了一个 S 锁。

可以得出结论:

对主键等值加锁,且值存在时,会对表添加意向锁,同时会对主键索引添加行锁。

主键等值查询 —— 数据不存在

mysql> select * from t where id = 11 for update;

如果是数据不存在的时候,会加什么锁呢?锁的范围又是什么?

在验证之前,分析一下数据的间隙。

浅谈MySQL next-key lock 加锁范围

  • id = 11 是肯定不存在的。但是加了 for update,这时需要加 next-key lock,id = 11 所属区间为 (10,15] 的前开后闭区间;
  • 因为是等值查询,不需要锁 id = 15 那条记录,next-key lock 会退化为间隙锁;
  • 最终区间为 (10,15) 的前开后开区间。

使用 data_locks 分析一下锁信息:

浅谈MySQL next-key lock 加锁范围

看下锁的信息 X,GAP 表示加了间隙锁,其中 LOCK_DATA = 15,表示锁的是 主键索引 id = 15 之前的间隙。

浅谈MySQL next-key lock 加锁范围

此时在另一个 Session 执行 SQL,答案显而易见,是 id = 12 不可以插入,而 id = 15 是可以更新的。

可以得出结论,在数据不存在时,主键等值查询,会锁住该主键查询条件所在的间隙。

主键范围查询(重点)

mysql> begin; select * from t where id >= 10 and id < 11 for update;

根据 《MySQL 45 讲》分析得出下面结果:

  • id >= 10 定位到 10 所在的区间 (10,+∞);
  • 因为是 >= 存在等值判断,所以需要包含 10 这个值,变为 [10,+∞) 前闭后闭区间;
  • id < 11 限定后续范围,则根据 11 判断下一个区间为 15 的前开后闭区间;
  • 结合起来则是 [10,15]。(不完全正确)

先看下 data_locks

浅谈MySQL next-key lock 加锁范围

可以看到除了表锁之外,还有 id = 10 的行锁(X,REC_NOT_GAP)以及主键索引 id = 15 之前的间隙锁(X,GAP)。

所以实际上 id = 15 是可以进行更新的。也就是说前开后闭区间出现了问题,个人认为应该是 id < 11 这个条件判断,导致不需要进行了锁 15 这个行锁。

浅谈MySQL next-key lock 加锁范围

结果验证也是正确的,id = 12 插入阻塞,id = 15 更新成功。

当范围的右侧是包含等值查询呢?

mysql> begin; select * from t where id > 10 and id <= 15 for update;

来分析一下这个 SQL:

id > 10 定位到 10 所在的区间 (10,+∞);id <= 15 定位是 (-∞, 15];结合起来则是 (10,15]。

同样先看一下 data_locks

浅谈MySQL next-key lock 加锁范围

可以看出只添加了一个主键索引 id = 15 的 X 锁。

验证下 id = 15 是否可以更新?再验证 id = 16 是否可以插入?

浅谈MySQL next-key lock 加锁范围

事实证明是没有问题的!

当然,这里有小伙伴会说,在 《MySQL 45 讲》 里面说这里有一个 bug,会锁住下一个 next-key。

浅谈MySQL next-key lock 加锁范围

事实证明,这个 bug 已经被修复了。修复版本为 MySQL 8.0.18。但是并没有完全修复!!!

参考链接地址:

https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-18.html

搜索关键字:Bug #29508068)

浅谈MySQL next-key lock 加锁范围

咱们可以分别用 8.0.17 进行复现一下:

浅谈MySQL next-key lock 加锁范围

在 8.0.17 中 id <= 15 会将 id = 20 这条数据也锁着,而在 8.0.25 版本中则不会。所以这个 bug 是被修复了的。

再来看下是前开后闭还是前开后开的问题,严谨一下,使用 8.0.17 和 8.0.18 做比较。

浅谈MySQL next-key lock 加锁范围

浅谈MySQL next-key lock 加锁范围

现在我估计大概率是在 8.0.18 版本修复 Bug #29508068 的时候,把这个前开后闭给优化成了前开后开了。

对比 data_locks 数据:

浅谈MySQL next-key lock 加锁范围

注意红色下划线部分,在 8.0.17 版本中 id < 17 时 LOCK_MODE 是 X,而在 8.0.25 版本中则是 X,GAP

总结

本文主要通过实际操作,对主键加锁时的 next-key lock 范围进行了验证,并查阅资料,对比版本得出不同的结论。

结论一:

  • 加锁时,会先给表添加意向锁,IX 或 IS;
  • 加锁是如果是多个范围,是分开加了多个锁,每个范围都有锁;(这个可以实践下 id < 20 的情况)
  • 主键等值查询,数据存在时,会对该主键索引的值加行锁 X,REC_NOT_GAP
  • 主键等值查询,数据不存在时,会对查询条件主键值所在的间隙添加间隙锁 X,GAP
  • 主键等值查询,范围查询时情况则比较复杂:
    • 8.0.17 版本是前开后闭,而 8.0.18 版本及以后,进行了优化,主键时判断不等,不会锁住后闭的区间。
    • 临界 <= 查询时,8.0.17 会锁住下一个 next-key 的前开后闭区间,而 8.0.18 及以后版本,修复了这个 bug。

优化后,导致后开,这个不知道是因为优化后,主键的区间会直接后开,还是因为是个 bug。具体小伙伴可以尝试一下。

结论二

通过使用 select * from performance_schema.data_locks; 和操作实践,可以看出 LOCK_MODE 和 LOCK_DATE 的关系:

 

LOCK_MODE LOCK_DATA 锁范围
X,REC_NOT_GAP 15 15 那条数据的行锁
X,GAP 15 15 那条数据之前的间隙,不包含 15
X 15 15 那条数据的间隙,包含 15

LOCK_MODE = X 是前开后闭区间;X,GAP 是前开后开区间(间隙锁);X,REC_NOT_GAP 行锁。

基本已经摸清主键的 next-key lock 范围,注意版本使用的是 8.0.25。

疑问

  • 那唯一索引的 next-key lock 范围是什么?
  • 当索引覆盖时锁的范围和加锁的索引分别是什么?
  • 我为什么说这个 bug 没有完全修复,也是在非主键唯一索引中复现了这个 bug​。

文章篇幅有限,小伙伴可以先自己思考一下,尽量自己操作试一试,实践出真知。至于具体答案,那就需要下一篇文章进行验证并总结结论了。

到此这篇关于浅谈MySQL next-key lock 加锁范围 的文章就介绍到这了,更多相关MySQL next-key lock 加锁范围 内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

MySQL 相关文章推荐
mysql多表查询-笔记七
Apr 05 MySQL
MySQL完整性约束的定义与实例教程
May 30 MySQL
mysql联合索引的使用规则
Jun 23 MySQL
浅谈mysql增加索引不生效的几种情况
Jun 23 MySQL
mysql 直接拷贝data 目录下文件还原数据的实现
Jul 25 MySQL
Mysql中一千万条数据怎么快速查询
Dec 06 MySQL
深入讲解数据库中Decimal类型的使用以及实现方法
Feb 15 MySQL
MySQL七大JOIN的具体使用
Feb 28 MySQL
MySQL数据库查询进阶之多表查询详解
Apr 08 MySQL
MySql数据库 查询时间序列间隔
May 11 MySQL
MySQL主从切换的超详细步骤
Jun 28 MySQL
mysql sock文件存储了什么信息
Jul 15 MySQL
MySQL为id选择合适的数据类型
MySQL单表千万级数据处理的思路分享
Jun 05 #MySQL
MySQL 时间类型的选择
Jun 05 #MySQL
MySQL索引失效的典型案例
Jun 05 #MySQL
MySQL库表名大小写的选择
Jun 05 #MySQL
mysql 带多个条件的查询方式
Mysql 如何实现多张无关联表查询数据并分页
Jun 05 #MySQL
You might like
如何使用PHP获取网络上文件
2006/10/09 PHP
用PHP读取超大文件的实例代码
2012/04/01 PHP
老司机传授Ubuntu下Apache+PHP+MySQL环境搭建攻略
2016/03/20 PHP
Ajax提交表单时验证码自动验证 php后端验证码检测
2016/07/20 PHP
php运行报错Call to undefined function curl_init()的最新解决方法
2016/11/20 PHP
php+ajax实现文件切割上传功能示例
2020/03/03 PHP
在chrome中window.onload事件的一些问题
2010/03/01 Javascript
JavaScript获得url所有参数键值表的方法
2015/03/21 Javascript
网页收藏夹显示ICO图标(代码少)
2015/08/04 Javascript
jquery实现可关闭的倒计时广告特效代码
2015/09/02 Javascript
js替换字符串中所有指定的字符(实现代码)
2016/08/17 Javascript
NodeJS收发GET和POST请求的示例代码
2017/08/25 NodeJs
vue复合组件实现注册表单功能
2017/11/06 Javascript
javascript与PHP动态往类中添加方法对比
2018/03/21 Javascript
JS实现的视频弹幕效果示例
2018/08/17 Javascript
jQuery实现鼠标移到某个对象时弹出显示层功能
2018/08/23 jQuery
vue router 源码概览案例分析
2018/10/09 Javascript
react-router 路由切换动画的实现示例
2018/12/03 Javascript
如何在微信小程序里面退出小程序的方法
2019/04/28 Javascript
jquery图片预览插件实现方法详解
2019/07/18 jQuery
vue 中的 render 函数作用详解
2020/02/28 Javascript
零基础写python爬虫之使用Scrapy框架编写爬虫
2014/11/07 Python
python实现聊天小程序
2018/03/13 Python
Python实现从SQL型数据库读写dataframe型数据的方法【基于pandas】
2019/03/18 Python
pip指定python位置安装软件包的方法
2019/07/12 Python
Python命令行参数argv和argparse该如何使用
2021/02/08 Python
小程序canvas中文字设置居中锚点
2019/04/16 HTML / CSS
韩国三星集团旗下时尚品牌官网:SSF SHOP
2016/08/02 全球购物
美国餐厅用品和厨房设备批发网站:KaTom Restaurant Supply
2018/01/27 全球购物
SQL Server面试题
2016/10/17 面试题
专科毕业生就业推荐信
2013/11/01 职场文书
家居设计专业个人自荐信范文
2013/11/26 职场文书
yy司仪主持词
2014/03/22 职场文书
个人安全生产责任书
2014/07/28 职场文书
教师反腐倡廉演讲稿
2014/09/03 职场文书
班干部竞选演讲稿(精选5篇)
2019/09/24 职场文书