这是十三个实验,扫到了MySQL锁的多个盲区(下)

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
云数据库 RDS MySQL Serverless,价值2615元额度,1个月
简介: 这是十三个实验,扫到了MySQL锁的多个盲区(下)

image.png

可以看到,可重复读级别下 update 的加锁与读提交不太一样,加锁的 lock_data 是 1,说明事务 G 扫描的 id 为 1 的记录之后没有释放锁。

如果把事务G、H 的启动顺序反过来,也就是先执行 H 的语句再执行 G 的语句,结果也是一样的,同样加锁的 lock_data 是 1,这说明可重复读的 update 不是先判断条件是否符合再上锁,而是先上锁再判断条件是否符合

update 都会被阻塞,最终结论就是:

可重复读级别下,加锁非索引列导致的全表记录上锁会使得所有插入和修改都会被阻塞。


小结一下:



此时把读者问题列上:



image.png


留言的回答语境是在可重复读级别下,现在我再来总结回答下:

在读提交级别下

如果锁定的列为非索引列,加锁都是加到主键索引上的,select ..for update的加锁的顺序是从前往后全表扫描的顺序,遍历的记录先上锁,上锁之后发现不满足条件,则释放锁,然后继续往后遍历,直到全表扫描结束。

insert 都不会被阻塞。

而 update 其它字段值,其实也是找记录,如果找到的记录已经被上锁了,那么就会阻塞,如果找到的记录没有被锁则不会被阻塞。

在可重复读级别下

如果锁定的列为非索引列,加锁都是加到主键索引上的,select ..for update的加锁的顺序是从前往后全表扫描的顺序,遍历的记录先上锁,上锁之后发现不满足条件,则不会释放锁,然后继续往后遍历,直到全表扫描结束。

所以只要有一个全表扫描的加锁,则 insert 的时候就会被阻塞。

update 加锁和select ..for update一致。

与之相关的还有一个问题:


image.png


图里已经有答案了,包括前面的截图也可以看到所有的 lock_type 都是 RECORD ,也就是行级锁。

实验三:隔离级别为读提交,锁定索引列的实验

此时在 name 列建立索引

CREATE TABLE `yes` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(45) DEFAULT NULL,
  `address` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4

同样准备数据如下:


image.png

可以看到,不会被阻塞,丝滑。

这个结果符合认知,因为此时 name 已经有索引了,在读提交级别下,只会在 name 索引上加相关记录的锁,而不会加全表行锁,因此事务 A、B 之间不会被阻塞。

此时再起一个事务 C,执行如下语句:


image.png


可以看到,锁的索引确实变成了 idx_name,lock_data 显示锁的是 yes 这个记录,id 为 1

从结果看:在可以命中二级索引的情况下,锁的是对应的二级索引

我们继续做实验。

将上面所有事务提交之后

启动事务 C 执行以下语句,且未提交事务:


image.png

执行 name 一样的插入,也不会阻塞。

所以在读提交级别下,对插入都不会产生阻塞。

关于 update 我就不实验了,和实验一的差别就是加锁索引换成了 name 的索引,其他表现一致。

实验四:隔离级别为可重复读,锁定索引列的实验

同样准备数据如下:


image.png

这是预期之内的阻塞,因为按照 name 为索引,yes这条记录是排在最后的(字母序),为了防止幻读,可重读隔离级别下会在对应记录前后加入间隙锁,而新的记录的插入恰巧需要排 yes 这条记录的后面。


image.png

但是从截图结果来看此时lock_mode是记录锁,且 lock_data 是 supremum,这又涉及到我的盲区了,难道是最后的记录插入比较特殊?所以不是因为间隙锁被阻塞,而是被最大记录行锁阻塞?

此时把事务A、B都提交了 ,然后我们再执行事务 C:


image.png

image.png

可以看到,此时被阻塞的锁是记录锁+间隙锁(next-key lock),这符合我们的认知和上面的图,因为要插入的数据在 yes 和公众号:yes的练级攻略之间。


小结


在命中索引列的前提下,只会在索引列上加锁。

如果此时在读已提交级别下:

select..for update和update的所查找的记录本身会被加上记录锁,因此这个位置的插入会被阻塞,其他位置的插入则没有影响。

如果此时在可重复读级别下:

select..for update和update的所查找的记录在索引位置前后会被加间隙锁,记录本身加记录锁,因此这些位置的插入会被阻塞,其他位置的插入则没有影响。


最后



分了四个实验大类,一个做了十三个实验。

还是挺有收获的,惊喜就是发现了细节盲区,之后研究一下再出一篇文章。

从实验来看,这里再做个概念性的总结:

  • 锁是作用在索引上的,因此如果能命中二级索引就在二级索引上加锁,不然就得被迫在聚簇索引上加锁。
  • 被迫在聚簇索引上加锁,会导致全表扫描式的加锁。
  • 在可重复读下,不论命中哪个索引,不论是select..for update还是update,只要被扫描到的记录,都会被加锁,不论是否符合条件,在事务提交之后才会释放。
  • 在读提交下,select..for update表现出来的结果是扫描到的记录先加锁,再判断条件,不符合就立马释放,不需要等到事务提交,而 update 的扫描是先判断是否符合条件,符合了才上锁。

声明:以上实验是基于 MySQL 5.7.26 版本,存储引擎为 InnoDB 。

这些实验我之前花了三个工作日晚上做的,由于时间是零散的,导致中间实验出错,期间设置事务隔离级别语句有问题,导致我在错误的前提下做实验,实验结果不断地冲击我的认知,我整个人都快搞崩溃了....

然后周六花了一天的时间重新理了一下,实验图很多,可能看了后面就忘了前面,建议结合着结论来回看,这样对结论会有更深刻的认识,但是有些实验结论我是根据实验现象来推断的,我没有去找相关的官网说明,如有错误,恳请指正,如有疑惑还请自行实验,可以在评论区交流一番。


相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
13天前
|
关系型数据库 MySQL 数据库
【MySQL实战笔记】 06 | 全局锁和表锁 :给表加个字段怎么有这么多阻碍?-01
【4月更文挑战第17天】MySQL的锁分为全局锁、表级锁和行锁。全局锁用于全库备份,可能导致业务暂停或主从延迟。不加锁备份会导致逻辑不一致。推荐使用`FTWRL`而非`readonly=true`因后者可能影响其他逻辑且异常处理不同。表级锁如`lock tables`限制读写并限定操作对象,常用于并发控制。元数据锁(MDL)在访问表时自动加锁,确保读写正确性。
65 31
|
7天前
|
SQL 存储 关系型数据库
MySQL锁详解
MySQL锁详解
15 0
|
8天前
|
算法 关系型数据库 MySQL
|
8天前
|
SQL 关系型数据库 MySQL
MySQL锁:解析隐式锁与显式锁
【4月更文挑战第20天】
31 0
|
8天前
|
关系型数据库 MySQL 数据库
|
9天前
|
SQL 关系型数据库 MySQL
MySQL 锁
MySQL 锁
12 1
|
15天前
|
关系型数据库 MySQL 索引
MySQL 锁机制
MySQL 锁机制
8 0
|
19天前
|
SQL 关系型数据库 MySQL
【MySQL系列笔记】隔离锁
MySQL 中的锁分为三种粒度:全局锁、表级锁和行级锁。全局锁用于锁定整个数据库,例如在进行全库逻辑备份时使用。表级锁分为表锁,元数据锁和意向锁;表锁又分为读锁(共享锁)和写锁(排他锁),用于锁定单表数据。元数据锁(MDL)在 DML 操作时自动加锁,确保事务的正确性;意向锁为了避免DML在执行时,加的行锁与表锁的冲突。行级锁分为记录锁、间隙锁和临键锁。记录锁锁定单行记录,间隙锁锁定索引记录之间的间隙,防止事务间隙插入,临键锁结合了记录锁和间隙锁,防止幻读并锁定数据和前一个间隙。
194 5
【MySQL系列笔记】隔离锁
|
22天前
|
关系型数据库 MySQL 数据库
MySQL锁三部曲:临键、间隙与记录的奇妙旅程
MySQL锁三部曲:临键、间隙与记录的奇妙旅程
18 0
|
1月前
|
存储 Java 关系型数据库
实验设备管理系统【GUI/Swing+MySQL】(Java课设)
实验设备管理系统【GUI/Swing+MySQL】(Java课设)
11 0