【MySql系列2】一文带你搞懂MySql的各种锁

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,高可用系列 2核4GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 上周五值班时遇到一个很诡异的线上问题,对表A的M行数据加排它锁后,然后批量插入数据到表B,结果导致A表和B表中其它的数据N加锁和插入数据耗时过长。如果是加的行锁,这个还好理解,但是A数据的加锁和更新,为什么会对B数据有影响呢?然后我同时提出两个假设:1. 大量插入数据到B表时,可能会将B表的行锁升级为表锁;2. 插入数据时会加插入意向锁,可能是插入意向锁阻塞了其它数据的加锁和更新。

SRNG2XCCU_XI2GY3$_$3E(I.jpg

内容包括共享锁S、排它锁X、意向共享锁IS、意向排它锁IX、行锁、间隙锁Gap Lock、Next-Key锁、插入意向锁。

A}5`(JEHNI]EQ@5RCZ`Z_VS.png

以后面试官再问你MySQL的锁,不要只会回答悲观锁和乐观锁了,感觉太Low!!!


前言


上周五值班时遇到一个很诡异的线上问题,对表A的M行数据加排它锁后,然后批量插入数据到表B,结果导致A表和B表中其它的数据N加锁和插入数据耗时过长。如果是加的行锁,这个还好理解,但是A数据的加锁和更新,为什么会对B数据有影响呢?

然后我同时提出两个假设:

  1. 大量插入数据到B表时,可能会将B表的行锁升级为表锁;
  2. 插入数据时会加插入意向锁,可能是插入意向锁阻塞了其它数据的加锁和更新。


内容回顾


之前学习MySQL,我知道共享锁、排它锁、行锁、间隙锁和Next-key锁,为了偷懒,我就直接贴几张之前写的PPT,首先看一下共享锁、排它锁:

image.gifOG}{78A}JQ`TIWD(M4P{TZ7.jpg

这个很简单,我们经常使用排它锁锁住该行,然后再去更新数据,避免“丢失更新”类的问题:

ND`OZ)K[WJ$}@JD_MR64UZP.png


那行锁、间隙锁和Next-key锁又是什么呢?

M6{ACSVYII%%@Y0K)GG[G]8.png

一般我们用for update加锁时,其实是加的Gap Lock,但是因为我们经常会将for update加到主键上,所以Gap Lock就降级为行锁。

这个很基础,仅作简单回顾,不懂的同学可以再去看一下书哈。


意向锁


基本概念

InnoDB 支持行锁和表锁,意向锁是是一种不与行级锁冲突的表级锁,这里有2点需要注意:

  • 意向锁是表锁;
  • 意向锁不会与行级锁冲突。

意向锁分为两种:

  • 意向共享锁(intention shared lock, IS):事务有意向对表中的某些行加共享锁(S锁)
-- 事务要获取某些行的 S 锁,必须先获得表的 IS 锁。 
SELECT column FROM table ... LOCK IN SHARE MODE;
  • 意向排他锁(intention exclusive lock, IX):事务有意向对表中的某些行加排他锁(X锁)
-- 事务要获取某些行的 X 锁,必须先获得表的 IX 锁。
 SELECT column FROM table ... FOR UPDATE;

意向锁是由数据引擎自己维护,用户无法手动操作意向锁,在为数据行加共享 / 排他锁之前,InooDB 会先获取该数据行所在在数据表的对应意向锁。


意向锁的兼容互斥性

意向锁之间的兼容互斥性:image.gif

1)A%9SIH}MN49IDUK}G~[%M.png

意向锁与普通的排他 / 共享锁的兼容互斥性:image.gif

FF)1I~`Q(76[)RV4Z$}FQWQ.png

注意:这里的排他 / 共享锁指的都是表锁!!!意向锁不会与行级的共享 / 排他锁互斥!!!

下面我们看一个问题,我们有下面一张表,里面数据如下:

4~ZLET5O6_ROE]JM}U571CJ.pngimage.gif

事务 A 获取了某一行的排他锁,并未提交:

VU{PA68WE3GR[0@8UHE6ILD.png

事务 B 想要获取 t_rule1 表的表锁:

8JZH)F%)C~~SIR@P@KJ_)S6.png

事务A持有意向排它锁,事务B想要去获取 t_rule1 表的表级排它锁,这两者明显互斥,所以事务B会被阻塞。

如果没有意向锁,会出现什么情况呢?事务B会去扫描 t_rule1 表,看是否每一行数据是否持有行级排它锁,当发现id=5加了行锁,事务B才会阻塞。

这就是为啥要加意向锁,其实主要是为了减少对行锁的扫描!嗯。。。我可以把意向锁理解为一个小红旗,当工厂里有人工作时,就把小红旗升上来,告诉工厂外面的人,有人在里面工作。当工厂里面没有工人工作时,就把小红旗降下来。这样外面的人就只需要通过小红旗判断是否有人在工厂工作,而不需要进入工厂,挨个排查是否有人在工作了。


意向锁不会影响行锁

意向锁只是对表锁而言,和行级别的共享锁 / 排它锁不会发生互斥!!!举个非常简单的例子,我们在A事务操作:

select * from t_rule1 where id = 5 for update;

该行获取2把锁,分别为意向排它锁和行级排它锁,我在B事务操作:

select * from t_rule1 where id = 10 for update;

该行需要获得意向排它锁和行级排它锁,由于事务B的意向排它锁和事务A的意向排它锁兼容,且和事务A的行级排它锁不会发生互斥,所以事务B的意向排它锁可以获取成功。

同理,事务B的行级排它锁也可以和事务A的两把锁兼容,为啥两个行级排它锁可以兼容呢?因为两个事务锁住的不是同一行!


意向锁小结

  • 意向锁之间,是互相兼容的;
  • 意向锁只和表级锁会冲突:意向锁(共享IS/排它IX)和表锁(共享S/排它X),除了IS和S兼容,其它全部互斥;
  • 意向锁不会与行级锁冲突:意向锁(共享IS/排它IX),不会和行级的X/S发送冲突;
  • 意向锁的作用:提高并发,减少行锁判断。


插入意向锁


不同事务数据插入

还是回到“前言”中提到的问题,当插入数据A时,会阻塞数据B的插入么,比如事务A中执行,未commit:

image.gifG`CI%IAFD7$X@~]B{$FYW7H.png

然后在事务B中执行:

image.gif`X3NGHE{L6TE$E%$8R866ZQ.png

当两个事务都提交后,两行数据都插入成功,所以事务A的插入,不会影响事务B的插入,除非插入的是同一行,比如事务A插入28,事务B也插入28,这时就会阻塞:

)%47BGGVD]5O36OXGIUEONK.png

这个只是稍微扩展了一下,大家可以自己去分析,如果这都不知道,证明你前面的知识还没有完全掌握。


基础知识

有同学可能会说,两个事务数据的插入,如果插入的不是同一行,当然不会影响,这个小学生都知道。那我再多问一句,我让你从锁的角度分析,为什么他们不会影响呢?这里就需要提到一个概念“插入意向锁”。

插入意向锁是在插入一条记录行前,由 INSERT 操作产生的一种间隙锁。该锁用以表示插入意向,当多个事务在同一区间(gap)插入位置不同的多条数据时,事务之间不需要互相等待。假设存在两条值分别为 4 和 7 的记录,两个不同的事务分别试图插入值为 5 和 6 的两条记录,每个事务在获取插入行上独占的(排他)锁前,都会获取(4,7)之间的间隙锁,但是因为数据行之间并不冲突,所以两个事务之间并不会产生冲突(阻塞等待)。

总结来说,插入意向锁的特性可以分成两部分:

  • 插入意向锁是一种特殊的间隙锁 —— 间隙锁可以锁定开区间内的部分记录。
  • 插入意向锁之间互不排斥,所以即使多个事务在同一区间插入多条记录,只要记录本身(主键、唯一索引)不冲突,那么事务之间就不会出现冲突等待。

需要强调的是,虽然插入意向锁中含有意向锁三个字,但是它并不属于意向锁而属于间隙锁,因为意向锁是表锁而插入意向锁是行锁。

再回到上面的多事务插入示例,事务A插入数据27时,获取到的是(25,30)的间隙锁和27的行锁,事务B插入数据28时,获取到的也是(25,30)的间隙锁和28的行锁。

因为行锁27和行锁28不是同一行,所以不会冲突,然后两个事务获取到的插入意向锁不会互相排斥,所以可以插入成功。

讲了这么多,感觉“插入意向锁”好像没啥用?真的是这样的么?


为什么不用间隙锁

刚才提到,插入意向锁,其实就是特殊的间隙锁,如果只是使用普通的间隙锁会怎么样呢?

还是回到刚才的示例,我们在事务A插入数据时:

insert into t_rule1(id,c,d) values(26,16,26);

其实会获取到3把锁:

  • id=26的行锁
  • 字段c在区间(15,16)的间隙锁
  • 字段c在区间(16,20)的间隙锁

最终,事务 A 插入了该行数据,并锁住了(15,20)这个区间。

随后事务 B 试图插入一行数据:

insert into t_rule1(id,c,d) values(27,17,26);

因为c=17位于(15,20)这个区间,而该区间内又存在一把间隙锁,所以事务 B 别说想申请自己的间隙锁了,它甚至不能获取该行的记录锁,自然只能乖乖的等待事务 A 结束,才能执行插入操作。

很明显,这样做事务之间将会频发陷入阻塞等待,插入的并发性非常之差。这时如果我们再去回想我们刚刚讲过的插入意向锁,就不难发现它是如何优雅的解决了并发插入的问题。


插入间隙锁小结

  • MySql InnoDB 在 Repeatable-Read 的事务隔离级别下,使用插入意向锁来控制和解决并发插入。
  • 插入意向锁是一种特殊的间隙锁。
  • 插入意向锁在锁定区间相同但记录行本身不冲突的情况下互不排斥。

我甚至可以简单理解为,插入意向锁是为了避免普通间隙锁导致的并发插入问题才引入的,其实就是告诉其它事务,我是一把插入意向锁,我的权利比普通间隙锁大,你们可以不用管它,想怎么插入,就怎么插入。


锁总结


这篇文章应该包含了MySql中排查问题时,需要考虑的锁:

  • 共享锁S:行级锁,读锁,读读不互斥,读写互斥;
  • 排它锁X:行级锁,写锁,写写互斥;
  • 行锁:锁住一行,通过排它锁X实现;
  • 间隙锁Gap Lock:锁住一个区间;
  • Next Key锁:行锁 + 间隙锁Gap Lock,锁住一个区间;
  • 意向共享锁IS:表级锁,和意向锁都兼容,和表级共享锁S兼容,和表级排它锁X互斥;
  • 意向排它锁IX:表级锁,和意向锁都兼容,和其它表级排互斥;
  • 插入意向锁:特殊间隙锁,和其它插入意向锁兼容。


后记


再回到“前言"中提到的那个问题,现在可以基本断定加锁和插入耗时过高,并不是插入意向锁导致。但是如果大量数据插入,会出现锁表的情况么,我感觉应该不会。


其实后来我们排查时,事务A还有大量数据更新的操作,因为索引是联合索引,然后命中的索引只命中了第一个字段(不知道MySQL内部又搞了啥优化),且区分度很低(历史的坑),导致数据更新非常慢,我们怀疑是这里的大量更新,导致行锁升级为表锁,当然这个也只是猜测,解决问题的方式就是需要调换联合索引的字段顺序,让区分度高的字段排在索引的第一列。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
相关文章
|
7月前
|
SQL AliSQL 关系型数据库
MYSQL的全局锁和表锁
本文介绍了MySQL中的锁机制,包括全局锁、表级锁及其应用场景。全局锁通过`Flush tables with read lock (FTWRL)`实现,主要用于全库逻辑备份,但会阻塞更新和结构变更操作。表级锁分为显式表锁(`lock tables`)和元数据锁(MDL),前者用于控制并发访问,后者自动加锁以确保读写正确性。文章还探讨了如何安全地为小表添加字段,建议通过设置DDL等待时间或使用MariaDB/AliSQL的NOWAIT/WAIT功能避免业务阻塞。这些方法有助于在高并发场景下优化数据库性能与安全性。
184 0
|
SQL 关系型数据库 MySQL
MySQL 锁
MySQL里常见的几种锁
226 3
|
10月前
|
关系型数据库 MySQL 网络安全
如何排查和解决PHP连接数据库MYSQL失败写锁的问题
通过本文的介绍,您可以系统地了解如何排查和解决PHP连接MySQL数据库失败及写锁问题。通过检查配置、确保服务启动、调整防火墙设置和用户权限,以及识别和解决长时间运行的事务和死锁问题,可以有效地保障应用的稳定运行。
402 25
|
11月前
|
存储 关系型数据库 MySQL
MySQL进阶突击系列(06)MySQL有几种锁?| 别背答案,现场演示一下
本文详细解析了MySQL InnoDB存储引擎的锁机制,涵盖读锁、写锁、意向锁、记录锁、间隙锁和临键锁等8种锁类型。重点探讨了不同锁类型的加锁与释放方式,以及事务并发场景下的实战验证。通过具体示例,展示了在不同情况下锁的行为及其对事务的影响。文章还特别强调了锁的作用范围主要是索引,并解释了锁如何影响数据的读写操作。最后总结了并发事务中加锁规则,帮助读者深入理解MySQL的锁机制。
|
存储 关系型数据库 MySQL
优化 MySQL 的锁机制以提高并发性能
【10月更文挑战第16天】优化 MySQL 锁机制需要综合考虑多个因素,根据具体的应用场景和需求进行针对性的调整。通过不断地优化和改进,可以提高数据库的并发性能,提升系统的整体效率。
720 1
|
关系型数据库 MySQL Java
MySQL数据锁:Record Lock,Gap Lock 和 Next-Key Lock
本文基于 MySQL 8.0.30 版本及 InnoDB 引擎,深入解析三种行锁机制:记录锁(Record Lock)、间隙锁(Gap Lock)和临键锁(Next-key Lock)。记录锁锁定索引记录,确保事务唯一修改;间隙锁锁定索引间的间隙,防止新记录插入;临键锁结合两者,锁定范围并记录自身,有效避免幻读现象。通过具体示例展示了不同锁的作用机制及其在并发控制中的应用。
1245 2
|
存储 关系型数据库 MySQL
MySQL数据库锁:共享锁和独占锁
本文详细介绍了`InnoDB`存储引擎中的两种行级别锁:共享锁(S锁)与排他锁(X锁)。通过具体示例展示了这两种锁的工作机制及其在`InnoDB`与`MyISAM`引擎中的表现差异。文章还提供了锁的兼容性矩阵,帮助读者更好地理解锁之间的互斥关系。最后总结了两种锁的特点及适用场景。适合希望深入了解`MySQL`并发控制机制的读者阅读。
486 1
|
监控 关系型数据库 MySQL
MySQL锁机制与解决死锁问题
MySQL锁机制与解决死锁问题
630 5
|
关系型数据库 MySQL 数据库
Mysql的锁
本文介绍了MySQL中表级锁和行级锁的区别,其中MyISAM仅支持表级锁,而InnoDB支持表级锁和行级锁,默认为行级锁。表级锁锁定整个表,实现简单,资源消耗少,但并发度低;行级锁仅锁定相关记录,减少冲突,提高并发度,但加锁开销大。此外,还介绍了共享锁和排他锁的概念及意向锁的作用。
127 1
|
存储 关系型数据库 MySQL
MySQL锁,锁的到底是什么?
【10月更文挑战第16天】MySQL 锁锁定的是与数据和资源相关的对象,其目的是为了保证数据的一致性、避免冲突,并在并发环境下合理协调事务或操作的执行。理解锁的对象和意义对于优化数据库性能、处理并发问题至关重要。
352 0

推荐镜像

更多