MySQL的加锁规则

这篇具有很好参考价值的文章主要介绍了MySQL的加锁规则。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

学习了MySQL的锁后,知道其有这么多锁,那应该会有些疑惑,这么多锁,究竟我在写sql语句时候用到哪个锁的,什么情况是用什么锁的?在哪里查看该sql语句是用了哪些锁的呢?加锁的规则是什么呢?这篇文章就来解决这些疑惑。

MySQL的默认存储引擎是innodb,而我们常用的也是innodb,所以我们主要研究的是行级锁行级锁有行锁、间隙锁、临键锁

间隙锁和临键锁的唯一目是为了解决幻读现象。在可重复读(RR)隔离级别解决了幻读问题。那很明显在RR隔离级别下是使用了间隙锁和临键锁。

间隙锁和临键锁只有在可重复读隔离级别中才会存在,如果是在RC读已提交隔离级别下,是没有间隙锁的存在,只有行锁

所以,我们只讨论在RR隔离级别的innodb引擎表的锁。

这里测试环境mysql版本:

mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.36    |
+-----------+
1 row in set (0.00 sec)

--测试使用的表和表数据
CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `c` int(11) DEFAULT NULL,
  `d` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `c` (`c`)
) ENGINE=InnoDB;
 
insert into t values(0,0,0),(5,5,5),
(10,10,10),(15,15,15),(20,20,20),(25,25,25);

下图中的规则是《MySQL45讲》专栏作者总结的。

MySQL的加锁规则,mysql,数据库

我的理解:

从我使用的MySQL版本(MySQL8.0.36)测试来看,一个"bug" 是已经修复的了。

首先要清楚:加锁是对索引加锁的,不是对该行记录加锁的。

比如select id from t where c=10 lock in share mode;是对字段c的索引加临键锁(5,10],不是对主键索引加锁。

而比如select * from t where c=10 lock in share mode;对字段c的索引加临键锁(5,10],查询的是所有字段,就需要回表,那就会用到主键查询,那就是会访问到主键嘛, 也对主键索引加锁,这就是原则2所讲的。

优化1中所讲的,因为是唯一索引,那就只需要查询到该索引等值,就可以直接返回这个数据,因为是唯一的,不需要再往后查找了。只需要锁住那条数据即可,那next-key lock就可以退化为行锁。

所以是要区分唯一索引和非唯一索引

优化2中,等值查询的向后遍历是什么意思?因为可能这个等值查询的数据是不存在的。

比如 select id from t where id=9。而id=9这个数据是不存在的 ,那主键加锁就加在9的前后两个数据之间:id=5和id=10,所以是加临键锁(5,10]。而这个是等值查询,就继续在(5,10]从9往后遍历,到最后一个数据id=10,10不满足条件,那就不锁10,就可以退化成间隙锁(5,10)。

我个人认为:只需记住2点:

一个原则:加锁的基本单位是 next-key lock。加锁是对索引加锁的

一个bug:非唯一索引范围查询,该索引的临键锁不会有退化为间隙锁或记录锁

其他就通过常理来分析推理即可

为什么说是一个bug,是因为非唯一索引的范围查询不符合常理分析推理

那接下来结合我总结的,用具体例子来讲解。

案例一:唯一索引(主键)等值查询

MySQL的加锁规则,mysql,数据库

红圈是sql语句输入的时间顺序。

分析过程:

  • 根据一个原则,加锁的基本单位是 next-key lock查询条件是id=7, 表中是没有id=7的数据,对主键加临键锁(5,10]。
  • 因为id=10是不符合条件的,所以可以退化成间隙锁(5,10)。

所以插入数据(8,8,8),即是id=8的数据被阻塞,因为id在(5,10)之间被锁住了,而更新id=10的数据是成功的。

上面的是我们的分析结果,那我们如何可以通过数据查看是否是加了这些表的呢?可以通过该语句

select * from performance_schema.data_locks\G;

来查看。

在执行了上图的第二步后,就执行该语句,可得

MySQL的加锁规则,mysql,数据库

说明在主键索引加的是间隙锁(5,10),这是符合我们分析得到的结果的。 

案例二:非唯一索引等值锁

MySQL的加锁规则,mysql,数据库

大家可能想到会话2应该是被阻塞的,而会话3是成功的,但结果却是相反的。

注意:普通的select...是不加锁的select ... lock in share mode 和 select ... for update才是加锁的lock in share mode是共享读锁,for update是独占写锁。

分析过程:

  • 根据一个原则,加锁的基本单位是 next-key lock,查询条件是c=5,给普通索引c加临键锁(0,5]。
  • 因为索引c不是唯一索引,那该值可能有多个,就需要继续往右查询,直到找到第一个不符合该值的值,那就是到了c=10,所以还需要给索引c加临键锁(5,10],而10是不符合条件的,所以退化成间隙锁(5,10)。这里非唯一索引的锁可以退化成间隙锁是因为这个查询不是范围查询(该查询条件是where c=5)。
  • 这里是select id,不需要回表,就不用给主键索引加锁。

 为什么索引c的临键锁(0,5]不退化成行锁?

MySQL的加锁规则,mysql,数据库

因为 c字段是非唯一索引,不具有唯一性,所以非唯一索引的索引结构都是和主键连结在一起的,而performance_schema.data_locks表中LOCK_DATA中就需要加上主键值。 所以如果只加记录锁(记录锁无法防止插入,只能防止删除或者修改),就会导致其他事务插入一条 (c=5,id=4) 的记录,这样前后两次查询的结果集就不相同了,出现幻读现象。

主键索引上没加锁,所以 会话2的 update .... where id=5语句不被阻塞。

但是在会话3中insert values(7,7,7),即是c=7的数据,而索引c的锁范围是(0,10),所以c=7会被锁住。

查看加锁情况

在上图的步骤2后执行select * from performance_schema.data_locks\G。

MySQL的加锁规则,mysql,数据库

 通过得知是对索引c加了临键锁(0,5]和间隙锁(5,10),总的即是锁住(0,10),符合我们分析得到的结果。

需要注意,在这个例子中,lock in share mode 只锁覆盖索引,但是如果是 for update 就不一样了。 执行 for update 时,系统会认为你接下来要更新数据,因此会顺便给主键索引上满足条件的行加上行锁。

另一种情况:

假如会话1中的select语句改成select * from t where c=5 lock in share mode,这个时候是查询所有字段,就会回表,那就需要主键索引,给主键索引加锁。

根据一个原则,加锁单位是临键锁,给主键加临键锁(0,5],而这里是等值查询,而且主键是唯一的,所以就只锁住id=5这行即可,即是退化成行锁。

案例三:主键索引范围锁

举例之前,你可以先思考一下这个问题:对于我们这个表 t,下面这两条查询语句,加锁范围相同吗?

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

id是int类型的,那这两条语句的逻辑是一样的。但是它们并不是等价的。

先来分析下第一条语句。

  • 根据原则1,加锁的单位是临键锁。查询条件是id=10,那给主键索引加临键锁(5,10]。
  • 主键是唯一的,那找到第一条id=10就会找到结果,不用再继续往右查找的。而表是有id=10这条数据的,所以不需要锁住(5,10],只需锁住10,这时就退化成行锁。

接着来看第二条语句

MySQL的加锁规则,mysql,数据库

 分析过程:

  • 根据原则1,加锁的单位是临键锁。
  • 查询条件id>=10部分。id=10部分,主键加临键锁(5,10]。主键索引也是唯一索引,表也有id=10这条数据,所以退化成行锁,锁住id=10。>10部分,主键加临键锁(10,+∞]。
  • 而id<11部分,(id=11的下行数据是id=15),主键加临键锁(-∞,15],因为最后一个是15,不符合id<11,所以就退化成间隙锁(-∞,15)。这里我们要清楚,临键锁退化成间隙锁,是只能去掉临键锁的右区间,不是直接变成(-∞,11)。
  • 所以结合(10,+∞]和(-∞,15)和行锁id10,变成锁住[10,15)。即主键加的锁:行锁id=10,间隙锁(10,15)。

备注:在《MySQL45讲中》,该作者实验的步骤5的更新语句是被阻塞,而我使用的MySQL版本显示是更新成功,没有被锁的。作者的分析 是主键加锁是行锁 id=10 和 next-key lock(10,15]。这个和现在我的测试结果不符合的。可能是MySQL版本不同导致的,请大家注意。

查看加锁情况

在上图的步骤2后执行select * from performance_schema.data_locks\G。

MySQL的加锁规则,mysql,数据库

 主键加锁:行锁id=10 和 间隙锁(10,15)符合我们分析得到的结果。

案例四:非唯一索引范围锁

该案例可以对照着案例三来看。与案例三不同的是,当前例子中查询语句的 where 条件中用的是字段 c(普通索引)。

这也是个困惑点,来验证我说的一个“bug”:非唯一索引范围查询,该索引的临键锁不会有退化为间隙锁或记录锁。这个bug主要是针对 查询条件是 非唯一索引<查询值 时候出现的,例如select ... where c<11。

MySQL的加锁规则,mysql,数据库

步骤4和5更新的是同一行数据的,但是通过索引c更新的被堵住,说明加锁的是对索引加锁的,不是对行数据加锁。

分析过程:

  • 根据一个原则,加锁的基本单位是临键锁。查询条件是c>=10 and c<11。对普通索引c加临键锁。
  • c>=10部分。c=10,对普通索引c加临键锁(5,10]。案例二中讲明白了这个不能退化成为行锁。c>10部分,对索引c加临键锁(10,+∞]。即c>=10部分,索引c加锁范围是(5,+∞]。
  • c<11部分,索引c加临键锁(-∞,15],按照常理来分析推理的话,15不符合条件,所以退化成间隙锁(-∞,15),但是MySQL对此不是这样操作的,这个就是我说的一个bug,这里符合非唯一索引的范围查询,索引不能退化成间隙锁,所以是(-∞,15]。
  • 所以综合(5,+∞]和(-∞,15],索引c的锁:临键锁(5,10],临键锁(10,15]。
  • 因为是select *,所以需要回表。表有c=10这条数据,所以要锁住c=10对应的主键索引,即是锁住主键10,对主键加行锁。

insert语句中字段c=8,在索引c的锁范围(5,15]内,会被阻塞。而通过字段c=15来update也会被阻塞。通过id=15来update就不会阻塞,因为主键索引只加了行锁(id=10)。

查看加锁情况

在上图的步骤2后执行select * from performance_schema.data_locks\G。

 可以看到第二个锁的范围是(10,15],这是包含15的,但是按照 select * from t where c>=10 and c<11 for update;的逻辑那肯定是不锁住c=15的,但是该实现的锁就是锁住了c=15,这就不符合常理。

MySQL的加锁规则,mysql,数据库

 更简单的,我们可以查看where c<11的加锁情况。

select * from t where c<11 for update;

--for update和lock in share mode 需要在事务中才能起效的,只是单单执行上面的语句是不起效的

begin;
select * from t where c<11 for update;

 select * from performance_schema.data_locks\G;查看锁情况。

按照常理分析,c=15是不符合条件的,所以就不应该锁住c=15的,但是结果却是临键锁,锁住了15。所以在对非唯一索引范围查询时有特别的做法, 不能退化成间隙锁。

MySQL的加锁规则,mysql,数据库

案例五:非唯一索引上存在”等值”的例子

该例子,是为了更好地说明“间隙”这个概念。这里,我给表 t 插入一条新记录。

mysql> insert into t values(30,10,30);

新插入的这一行 c=10,也就是说现在表里有两个 c=10 的行。那么,这时候索引 c 上的间隙是什么状态了呢?而由于非唯一索引上包含主键的值,所以是不可能存在“相同”的两行的。

MySQL的加锁规则,mysql,数据库

可以看到字段c是有两个10的,但是他们的主键是不一样的。所以这两个 c=10 的记录之间,也是有间隙的。

这次我们用 delete 语句来验证。注意,delete 语句加锁的逻辑,是加排他锁,其实跟 select … for update 是类似的。

MySQL的加锁规则,mysql,数据库

分析过程:

  • 根据一个原则,加锁的基本单位是临键锁。条件是c=10,对普通索引c加临键锁(5,10]。
  • 而这不是唯一索引,所以需要继续完后搜索,直到搜索到c=15才停止,这里就加临键锁(10,15],因为15不符合条件的,所以可以退化成间隙锁(10,15)。所以对普通索引c加的锁:临键锁(5,10],间隙锁(10,15)。
  • 表中是有c=10这条数据的,那就需要回表,找到id=10和id=30符合条件,所以主键需要加两个行锁,锁id=10和id=30。

更新语句条件是c=15,没在普通索引c的加锁范围内,所以成功。而第三步的插入语句中插入了c=12的语句,所以被阻塞。

接着来看看步骤5,6是怎情况。步骤5插入数据(4,5,100)不阻塞,步骤6插入数据(6,5,100)被阻塞。100对应的字段是没有索引的,可以不用关注。

而明明普通索引c的加锁范围是(5,15),是不包括5的,为什么数据(6,5,100)会被阻塞的呢?

那明显是在主键上不同导致的插入阻塞的。

这时我们来看看普通索引c的加锁范围吧,中蓝色区域覆盖的部分就是加锁的范围。

MySQL的加锁规则,mysql,数据库

为什么索引c的加锁范围变成这样呢?就是因为索引c不是唯一索引,可以有多个c=10,而c=10对应的多条数据的主键却是是不能相同的,所以加锁才会变成这样的。

从上图就可以看出来,在插入数据时候,id>5(比如6)就会被阻塞;id<5(比如4)就不被阻塞。

所以在插入数据时,能否插入成功,可能需要结合普通索引和主键索引来具体分析的。

查看加锁情况

在上面的步骤2后执行select * from performance_schema.data_locks\G;。

从这就可以看出,需要通过主键才能确定普通索引对应的数据。

MySQL的加锁规则,mysql,数据库

案例六:limit 语句加锁

该例子对照案例五,在案例五的基础上limit 2。

MySQL的加锁规则,mysql,数据库

这个例子里,左边会话 的 delete 语句加了 limit 2。而表 t 里 c=10 的记录其实只有两条,因此加不加 limit 2,删除的效果都是一样的,但是加锁的效果却不同。可以看到,右边会话 的 insert 语句执行通过了(案例5中也是插入(12,12,12),但是插入阻塞)。

分析过程:

  • 案例5中分析出来索引c的锁范围是(5,15),但是现在案例6中是limit2,其只需要两条数据。
  • 在锁(5,15)范围内找到了符合条件的2条数据,这里到了(c=10,id=30),但还没到锁范围的右边界,但是因为已经找齐数据了,就不再需要后面的锁了。这是很符合常理的,让所需的锁范围尽量小,而又不破坏业务需求。

MySQL的加锁规则,mysql,数据库

可以看到,(c=10,id=30)之后的右边间隙并没有在加锁范围里,因此 insert 语句插入 c=12 是可以执行成功的。

这个例子对我们实践的指导意义就是,在删除数据的时候尽量加 limit。这样不仅可以控制删除数据的条数,让操作更安全,还可以减小加锁的范围。

案例七:非索引的查询

字段d是没有创建索引的

MySQL的加锁规则,mysql,数据库

通过无索引字段d来进行查询,右边会话被阻塞。

查看加锁情况

MySQL的加锁规则,mysql,数据库

 每一条记录的索引上都会加 next-key 锁,都是锁住主键,这样就相当于锁住的全表。

注意的是:不是加上表锁的。

不只是锁定读查询语句不加索引才会导致这种情况,update 和 delete 语句如果查询条件不加索引,那么由于扫描的方式是全表扫描,于是就会对每一条记录的索引上都会加 next-key 锁,这样就相当于锁住的全表。

总结

加锁的规则:

一个原则:加锁的基本单位是 next-key lock。加锁是对索引加锁的

一个bug:非唯一索引范围查询,该索引的临键锁不会有退化为间隙锁或记录锁

其他就通过常理来分析推理即可

查询的情况就那几种:

唯一索引等值查询

唯一索引范围查询

非唯一索引等值查询

非唯一索引范围查询

非索引查询文章来源地址https://www.toymoban.com/news/detail-839115.html

到了这里,关于MySQL的加锁规则的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 关系数据库-9-[mysql8]中的语法校验规则sql_mode应用

    Mysql中sql_mode使用详解 sql_mode是一组mysql的语法校验规则,定义了mysql应该支持的sql语法、数据校验等。 1.3.1 ANSI模式 这是一种宽松模式,该模式下,会对所操作数据进行校验,如果不符合校验规则,数据会按照规则执行,并报warning警告。 1.3.2 STRICT_TRANS_TABLES模式 这是一种严格

    2024年02月02日
    浏览(46)
  • 【MySQL 】MySQL 创建数据库, MySQL 删除数据库,MySQL 选择数据库

    作者简介: 辭七七,目前大一,正在学习C/C++,Java,Python等 作者主页: 七七的个人主页 文章收录专栏: 七七的闲谈 欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖💖 我们可以在登陆 MySQL 服务后,使用 create 命令创建数据库,语法如下: 以下命令简单的演示了创建数据库的过程,

    2024年02月13日
    浏览(87)
  • 【MySQL数据库】初识MySQL数据库、安装MySQL

    在今天的数字化世界中,数据是企业和个人的重要资产。管理和存储数据变得至关重要,而 MySQL 数据库是一种备受欢迎的开源关系型数据库管理系统,它提供了稳定、可靠、高性能的数据存储解决方案。本文将介绍 MySQL 数据库的基本概念和安装过程,以便初学者能够轻松上

    2024年02月08日
    浏览(62)
  • 【MySQL数据库】MySQL数据库管理

    Structure Query Language(结构化查询语言)简称SQL,它被美国国家标准局(ANSI)确定为关系型数据库语言的美国标准,后被国际化标准组织(ISO)采纳为关系数据库语言的国际标准。数据库管理系统可以通过SQL管理数据库;定义和操作数据,维护数据的完整性和安全性。 数据:(data)

    2024年02月08日
    浏览(49)
  • 初识MySQL数据库——“MySQL数据库”

    各位CSDN的uu们你们好呀,小雅兰好久没有更文啦,确实是心有余而力不足,最近学习的内容太难了,这篇博客又是小雅兰的新专栏啦,主要介绍的是一些MySQL数据库的知识点,下面,让我们进入初识MySQL数据库的世界吧 为什么要使用数据库 数据库与数据库管理系统 MySQL介绍

    2024年02月06日
    浏览(50)
  • MySQL数据库:数据库管理系统与安装MySQL数据库

    目录 一、理论 1.数据库管理系统 2.关系型数据库 3.数据库 4.MySQL数据库 5.MySQL部署 二、实验 1.yum安装MySQL 2.编译安装MySQL 3.配置MySQL数据库的Tab补全  三、问题 1.数据库登录报错 2.数据库密码复杂度报错 3.数据库连接报错 四、总结 (1)概念 数据库管理系统(Database Management

    2024年02月12日
    浏览(62)
  • 初识mysql数据库之mysql数据库安装(centos)

    目录 一、卸载不需要的环境 二、安装mysql yum源 三、安装mysql 四、登录mysql 1. 直接登录 2. 设置免密码登录 五、配置my.cnf 六、mysql登录时的一些选项介绍 要注意,在安装mysql数据库时,最好将用户切换为root,避免一些不必要的问题。当数据库安装好后,普通用户也可以使用的

    2024年02月03日
    浏览(51)
  • MySQL笔记——MySQL数据库介绍以及在Linux里面安装MySQL数据库,对MySQL数据库的简单操作,MySQL的外接应用程序使用说明

    MySQL笔记——MySQL数据库介绍以及在Linux里面安装MySQL数据库,对MySQL数据库的简单操作,MySQL的外接应用程序使用说明 MySQL笔记——表的分组查询、表的分页查询、表的约束、数据库设计 MySQL案例——多表查询以及嵌套查询 MySQL笔记——数据库当中的事务以及Java实现对数据库进

    2024年01月16日
    浏览(70)
  • 【MySQL】使用DBeaver数据库管理工具进行MySQL数据库连接

    一、数据库连接信息填写 1、服务器地址:填写服务器部署的地址,以及端口号 2、数据库:sys 3、用户名:root 4、密码:服务器上面设置的具体密码 以上信息填写错误的报错提示 :Access denied for user ‘XXX’@’%’ to database ‘10.42.67.22’ 二、数据库说明 1、数据库连接时选择的

    2024年02月09日
    浏览(86)
  • MySQL示例数据库(MySQL Sample Databases) 之 sakila数据库

    官方示例数据介绍 MySQL 官方提供了多个示例数据库,在MySQL的学习、开发和实践中具有非常重要的作用,能够帮助初学者更好地理解和应用MySQL的各种功能和特性。 官方示例数据具体列表如下: 这些数据库都可以通过如下官方网址进行下载和安装 sakila数据库 可以在MySQL官方

    2024年02月05日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包