我做了一天的实验!

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
云数据库 RDS PostgreSQL,高可用系列 2核4GB
简介: 对记录加锁时,加锁的基本单位是 next-key lock,它是由记录锁和间隙锁组合而成的,next-key lock 是前开后闭区间,而间隙锁是前开后开区间。但是,next-key lock 在一些场景下会退化成记录锁或间隙锁。那到底是什么场景呢?今天,我们就以下面这个表来进行实验说明。

大家好,我是小林。


在前一篇文章我讲了下 MySQL 的全局锁、表记锁和行级别锁,其中行级锁只提了概念,并没有具体说。


因为行级锁加锁规则比较复杂,不同的场景,加锁的形式还不同,所以这次就来好好介绍下行级锁。


对记录加锁时,加锁的基本单位是 next-key lock,它是由记录锁和间隙锁组合而成的,next-key lock 是前开后闭区间,而间隙锁是前开后开区间。但是,next-key lock 在一些场景下会退化成记录锁或间隙锁。

那到底是什么场景呢?今天,我们就以下面这个表来进行实验说明。

28.png

其中,id 是主键索引(唯一索引),b 是普通索引(非唯一索引),a 是普通的列。注意,我的 MySQL 的版本是 8.0.26,不同版本的加锁规则可能是不同的27.png


唯一索引等值查询


当我们用唯一索引进行等值查询的时候,查询的记录存不存在,加锁的规则也会不同:

  • 当查询的记录是存在的,在用「唯一索引进行等值查询」时,next-key lock 会退化成「记录锁」
  • 当查询的记录是不存在的,在用「唯一索引进行等值查询」时,next-key lock 会退化成「间隙锁」

接下里用两个案例来说明。

先看看记录是存在的。

来看下面这个例子:

26.png

会话1加锁变化过程如下:

  1. 加锁的基本单位是 next-key lock,因此会话1的加锁范围是(8, 16];
  2. 但是由于是用唯一索引进行等值查询,且查询的记录存在,所以 next-key lock 退化成记录锁,因此最终加锁的范围是 id = 16 这一行

所以,会话 2 在修改 id=16 的记录时会被锁住,而会话 3 插入 id=9 的记录可以被正常执行。

接下来,看看记录不存在的情况

来看看,下面这个例子:

25.png

会话1加锁变化过程如下:

  1. 加锁的基本单位是 next-key lock,因此主键索引 id 的加锁范围是(8, 16];
  2. 但是由于查询记录不存在,next-key lock 退化成间隙锁,因此最终加锁的范围是 (8,16)。

所以,会话 2 要往这个间隙里面插入 id=9 的记录会被锁住,但是会话 3 修改 id =16 是可以正常执行的,因为 id = 16 这条记录并没有加锁。


唯一索引范围查询


范围查询和等值查询的加锁规则是不同的。举个例子,下面这两条查询语句,查询的结果虽然是一样的,但是加锁的范围是不一样的。

select * from t_test where id=8 for update;
select * from t_test where id>=8 and id<9 for update;

做个实验就知道了。

24.png

会话 1 加锁变化过程如下:

  1. 最开始要找的第一行是 id = 8,因此 next-key lock(4,8],但是由于 id 是唯一索引,且该记录是存在的,因此会退化成记录锁,也就是只会对 id = 8 这一行加锁;
  2. 由于是范围查找,就会继续往后找存在的记录,也就是会找到 id = 16 这一行停下来,然后加 next-key lock (8, 16],但由于 id = 16 不满足 id < 9,所以会退化成间隙锁,加锁范围变为 (8, 16)。

所以,会话 1 这时候主键索引的锁是记录锁 id=8 和间隙锁(8, 16)。会话 2 由于往间隙锁里插入了 id = 9 的记录,所以会被锁住了,而 id = 8 是被加锁的,因此会话 3 的语句也会被阻塞。由于 id = 16 并没有加锁,所以会话 4 是可以正常被执行。


非唯一索引等值查询


当我们用非唯一索引进行等值查询的时候,查询的记录存不存在,加锁的规则也会不同:

  • 当查询的记录存在时,除了会加 next-key lock 外,还额外加间隙锁,也就是会加两把锁
  • 当查询的记录不存在时,只会加 next-key lock,然后会退化为间隙锁,也就是只会加一把锁。

接下里用两个案例来说明。

我们先来看看查询的值存在的情况。

比如下面这个例子:

23.png

会话 1 加锁变化过程如下:

  1. 先会对普通索引 b 加上 next-key lock,范围是(4,8];
  2. 然后因为是非唯一索引,且查询的记录是存在的,所以还会加上间隙锁,规则是向下遍历到第一个不符合条件的值才能停止,因此间隙锁的范围是(8,16)。

所以,会话1的普通索引 b 上共有两个锁,分别是 next-key lock (4,8] 和间隙锁 (8,16) 。那么,当会话 2 往间隙锁里插入 id = 9 的记录就会被锁住,而会话 3 和会话 4 是因为更改了 next-key lock 范围里的记录而被锁住的。然后因为 b = 16 这条记录没有加锁,所以会话 5 是可以正常执行的。

接下来,我们看看查询的值不存在的情况

直接看案例:

22.png

会话 1 加锁变化过程如下:

  1. 先会对普通索引 b 加上 next-key lock,范围是(8,16];
  2. 但是由于查询的记录是不存在的,所以不会再额外加个间隙锁,但是 next-key lock 会退化为间隙锁,最终加锁范围是 (8,16)。

会话 2 因为往间隙锁里插入了 b = 9 的记录,所以会被锁住,而 b = 16 是没有被加锁的,因此会话 3 的语句可以正常执行。


非唯一索引范围查询


非唯一索引和主键索引的范围查询的加锁也有所不同,不同之处在于普通索引范围查询,next-key lock 不会退化为间隙锁和记录锁。来看下面这个案例:21.png

会话 1 加锁变化过程如下:

  1. 最开始要找的第一行是 b = 8,因此 next-key lock(4,8],但是由于 b 不是唯一索引,并不会退化成记录锁。
  2. 但是由于是范围查找,就会继续往后找存在的记录,也就是会找到 b = 16 这一行停下来,然后加 next-key lock (8, 16],因为是普通索引查询,所以并不会退化成间隙锁。

所以,会话 1 的普通索引 b 有两个 next-key lock,分别是 (4,8] 和(8, 16]。这样,你就明白为什么会话 2 、会话 3 、会话 4 的语句都会被锁住了。


总结


这次我以 MySQL 8.0.26 版本做了几个实验,让大家了解了唯一索引和非唯一索引的行级锁的加锁规则。这里需要注意的是,不同的版本加锁规则可能会有所不同。我这里总结下, 我这个 MySQL 版本的行级锁的加锁规则。唯一索引等值查询:

  • 当查询的记录是存在的,next-key lock 会退化成「记录锁」。
  • 当查询的记录是不存在的,next-key lock 会退化成「间隙锁」。

非唯一索引等值查询:

  • 当查询的记录存在时,除了会加 next-key lock 外,还额外加间隙锁,也就是会加两把锁。
  • 当查询的记录不存在时,只会加 next-key lock,然后会退化为间隙锁,也就是只会加一把锁。

非唯一索引和主键索引的范围查询的加锁规则不同之处在于:

  • 唯一索引在满足一些条件的时候,next-key lock 退化为间隙锁和记录锁。
  • 非唯一索引范围查询,next-key lock 不会退化为间隙锁和记录锁。

这些加锁规则其实很好总结的,大家自己可以用我文中的案例测试一遍,看一下你的 MySQL 版本和我的 MySQL 版本的加锁规则有什么不同。就说到这啦, 我们下次见啦!

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
消息中间件 安全 Java
【RabbitMQ高级篇】消息可靠性问题
【RabbitMQ高级篇】消息可靠性问题
292 0
|
关系型数据库 MySQL Java
MySQL单表膨胀优化之MyCat分库分表
MySQL单表膨胀优化之MyCat分库分表
253 0
|
4月前
|
网络协议 前端开发
为什么异步会导致HTTP队首阻塞?
总的来说,异步操作可能会导致HTTP队首阻塞,但是通过使用HTTP/2的多路复用技术,我们可以有效地解决这个问题。
122 16
|
5月前
|
安全 Java API
利用 AWS Signature:REST API 认证的安全指南
本文探讨了 AWS Signature 在保护 REST API 访问中的重要性,详解其工作原理,并提供 Java 和 Go 的实现示例。AWS Signature 通过加密技术确保请求安全,具备增强安全性、保障请求完整性、防范重放攻击及与 AWS 兼容等优势。文章还介绍了测试工具如 APIPost、Postman 和 cURL 的使用方法,帮助开发者验证实现效果。总结指出,采用 AWS Signature 可有效提升 API 安全性,增强用户信任并保护敏感数据。
|
11月前
|
人工智能 安全 BI
2024年度云治理企业成熟度发展报告解读(一)云市场发展洞察
从2023年开始,阿里云携手埃森哲每年发布《云治理企业成熟度年度发展报告》,通过数据来解读企业在云上的稳定性、安全、效率、成本等方面的发展现状,并反映各行业头部客户的技术演进趋势。该报告已成为了解中国云计算行业发展趋势的重要参考。这次,埃森哲将带来最新出炉的2024年度发展趋势报告(导读版)解读。
2024年度云治理企业成熟度发展报告解读(一)云市场发展洞察
|
10月前
|
Java 开发工具 Android开发
Eclipse 快捷键
Eclipse 快捷键
179 4
|
10月前
|
监控 API 云计算
云计算成本优化:AWS Cost Explorer与预算管理的艺术
【10月更文挑战第26天】随着云计算的发展,企业纷纷将业务迁移到云端,但云成本管理成为一大挑战。AWS作为领先的云服务提供商,提供了AWS Cost Explorer和预算管理工具,帮助企业有效监控、分析和优化云成本。通过这些工具,企业可以深入了解成本结构,设置预算目标,并在超支时及时采取措施,实现成本优化。示例代码展示了如何使用AWS Cost Explorer获取和分析成本数据。
160 5
|
网络协议 Linux
Linux查看端口监听情况,以及Linux查看某个端口对应的进程号和程序
Linux查看端口监听情况,以及Linux查看某个端口对应的进程号和程序
1130 2
二次元自适应个人导航页源码
二次元自适应个人导航页源码,动态风格,源码由HTML+CSS+JS组成,记事本打开源码文件可以进行内容文字之类的修改,双击html文件可以本地运行效果,感兴趣的可以去继续优化一下!
206 3
二次元自适应个人导航页源码
|
SQL 运维 监控
Nest.js 实战 (十):使用 winston 打印和收集日志记录
这篇文章介绍了在Nest服务中如何使用Winston记录日志。文章首先强调了日志记录在后台服务中的重要性,接着提到Nest默认的内部日志记录器,并指出可以通过@nestjs/common包中的Logger类来全面控制日志系统的行为。文章还提到,为了在生产环境中实现更高级的日志功能,可以使用如Winston之类的Node.js日志包。接下来,文章介绍了如何在Nest服务中使用Winston记录日志,包括安装相关依赖、创建winston配置文件以及实现简单的日志记录示例。最后,文章指出更高级的自定义日志功能需要读者自己去探索。
440 0
Nest.js 实战 (十):使用 winston 打印和收集日志记录