什么是MySQL锁?有哪些锁类型?

又到一年逐渐消失的'金三银四',相信准备有所行动的你已经非常兴奋了,别激动,冷静一下!

你应该准备很充分了吧,来个基础的,你看看这一连串的MySQL锁问题你能接住几个先

  • 面试官👨‍⚖️:你好呀,小伙子
  • 我🧑‍💻:嗯嗯,你好你好
  • 面试官👨‍⚖️:建立上说熟悉MySQL锁,那你讲讲MySQL有哪些锁
  • 我🧑‍💻:表锁、行锁...,心想我这是背过多少遍了
  • 面试官👨‍⚖️:什么是意向锁、auto-inc锁,有啥作用呢?
  • 我🧑‍💻:我知道,但为什么要加呢、、、
  • 面试官👨‍⚖️:那换一个好吧,举个说说行锁的加锁原理?
  • 我🧑‍💻:完了、我还不知道呀,对呀,既然加了锁,那
  • 面试官👨‍⚖️:那死锁知道吧,举个栗子,如何避免死锁展开讲讲?
  • 我🧑‍💻:好,那我举一个吧,心里已经萌生退意了

怎么样你接住了几招,接下来小许将分几期讲MySQL锁的基础、加锁原理、死锁带你把锁了解清楚!

为什么需要引入锁

锁是计算机协调多个进程或线程并发访问某一资源的一种机制,在并发事务下保证数据的正确和唯一性。

锁在 MySQL 中是非常重要的一部分,对 MySQL 的数据访问并发有着举足轻重的影响

MySQL中的锁是在服务器层或存储引擎层实现的,不同的存储引擎的锁机制也有较大的区别。

MySQL锁的实现

很多人都一样,在刚开始学习MySQL中锁的时候,网上一查出来一堆,什么表锁、行锁、读锁、写锁、悲观锁、乐观锁等等等,直接整个人就懵了。

本文我们将以锁粒度的角度去看MySQL锁的分类情况

没事,先看看小许归纳的锁知识大纲,先对锁的位置和锁归属的存储引擎有个前置了解!

全局锁

全局锁就是对整个数据库实例加锁,MySQL有个全局读锁的命令如下:

arduino 复制代码
flush tables with read lock(FTWRL)

执行后,整个数据库就处于只读状态(不能写入) 了,这个时候其他线程执行数据更新语句(数据的增删改),数据定义语句(建表、修改表结构等)等,都会被阻塞。

解锁命令:

复制代码
unlock tables

使用场景举例:

主要应用于做全库逻辑备份,原理也很简单在全局锁期间数据或表结构不会被更新,备份后文件的数据与预期也就一样了。

当时加上全局锁,意味着整个数据库都是只读状态,如果备份时间过长就导致其他

Mysql中数据备份使用的命令是mysqldump命令

使用参数-single-transaction的时候,导出数据之前就会启动一个事务,来确保拿到一致性视图,而由于MVCC的支持,这个过程中数据是可以正常更新的,因为读取的数据在更新前已确认。

页锁

页级锁是 MySQL 中比较独特的一种锁定级别,主要应用于 BDB 存储引擎,我们实际中基本上用的是InnoDB引擎,这里对页锁就不多展开了。

表锁

MyISAM和InnoDB都支持表级锁,但是InnoDB默认的是行级锁。

表锁下面又分了以下四种

表锁

顾名思义,就是直接对表进行加锁,可以使用下面命令:

arduino 复制代码
//加读锁
lock tables table_name read;
//加写锁
lock tables table_name write;
// 释放当前会话的所有表锁
unlock tables

如果加的是写锁,当对表进行写操作时也会被阻塞,直到写锁被释放。

不过尽量避免在使用 InnoDB 引擎的表使用表锁,因为表锁的颗粒度太大,会影响并发性能。

元数据锁

MySQL5.5引入了元数据锁(meta data lock - MDL),它不需要显式使用,在访问一个表的时候会被自动加上。

  • 对表数据进行 CRUD 操作时,加 MDL 读锁
  • 对表结构变更操作的时候,加 MDL 写锁

既然是自动加锁,那释放也是自动的!

事务执行期间,MDL 是一直持有的, 在事务提交后MDL才会释放。

意向锁(Intention Lock)

意向锁主要是在对数据表的行记录加共享锁(S锁)、独占锁(X锁)之前,需要先在表级别加上一个意向锁

在InnoDB引擎中,当执行查询操作,需要先对表加上「意向共享锁」,然后对该记录加【共享锁】

意向锁有两种类型:

意向共享锁(IS锁):一个事务给一个数据行加共享锁时,必须先获得表的意向共享锁

意向独占锁(IX锁): 一个事务给一个数据行加独占锁时,必须先获得表的意向独占锁

为什么需要先加意向锁?

意向锁的目的是更加快速的判断数据表表里是否有记录被加锁。

比如我们要加【独占表锁】,先在表级别加了【意向独占锁】,那么在加【独占锁】时,直接查该表是否有意向独占锁,如果有就表示表记录存在独占锁,这样就不用去遍历表记录去查看行记录是否存在独占锁了。

加锁命令如下:

csharp 复制代码
//加上意向共享锁,然后对读取的记录加共享锁
select ... lock in share mode;

//先表上加上意向独占锁,然后对读取的记录加独占锁
select ... for update;

AUTO-INC锁

字面意思是用来控制自动自增的锁?

是的,一般来说我们会在表中设置一个字段声明 AUTO_INCREMENT 的自增ID字段。

AUTO-INC锁在自增字段起了个什么作用呢?

当使用INSERT语句插入一条新记录时,MySQL会自动为自增字段加锁,防止其他并发的插入操作同时获取相同的自增值。

其他事务要等待,直到执行完插入语句之后才会释放锁。

这就保证了数据表的 AUTO_INCREMENT 字段的值是连续递增。

好吧,原来这个AUTO_INC锁的作用是这样的,以前我还一直不知道呢!

👉 AUTO-INC锁有什么问题?

大批量数据在一条语句中插入时(INSERT SELECT ),会带来一些性能上的影响,从而阻塞其他事务的插入操作!

🚩 MySQL是如何进行AUTO-INC锁性能优化的?

MYSQL 5.1.22版本开始,InnoDB存储引使用一种轻量级互斥锁(Mutex)来控制自增列增长

通过参数innodb_autoinc_lock_mode来控制 可以设定3个值分别是0,1,2

  • 0:traditional 每次insert都采用 AUTO-INC 锁,语句执行结束后才释放锁,但并发能力较弱
  • 1:consecutive 对于SIMPLE INSERT,使用轻量级互斥锁,对于BULK INSERT,使用AUTO-inc locking
  • 2:interleaved 采用轻量级锁,申请自增主键后就释放锁,但可能会造成insert分配的id顺序不一致

🚩 一个事务中存在多个insert语句,auto-inc锁是如何申请的?

自增锁更事务无关,即使多个insert语句在同一个十五中,每个insert还是都会申请罪行的自增锁。

行锁

顾名思义,行锁就是给数据库表中每行数据加锁,行锁是加在索引上

比如某个表中id字段是主键,如果给id=2这条记录加锁,那这把锁是加在主键索引(聚簇索引)上的

行锁使用分类

我们讲表锁的时候说到了意向锁,在对数据表的行记录加共享锁(S锁)、独占锁(X锁)之前,需要先在表级别加上一个意向锁 。

InnoDB 行级锁按照使用方式分为:共享锁(S锁)、排它锁(X锁)

读锁会阻塞写(X),但是不会堵塞读(S),而写锁则会把读(S)和写(X)都堵塞

对于普通 select 语句,innodb 不会加任何锁。如果想在select操作的时候加上 S锁 或者 X锁,需要我们手动加锁。

csharp 复制代码
//查询记录加共享锁
select ... lock in share mode;

//查询记录加独占锁
select ... for update;

InnoDB 在RR(MySQL默认隔离级别) ,对于 update、delete 和 insert 语句, 会自动给涉及的数据集加排它锁(X)

InnoDB支持3种行锁的算法,分别是:

  • Record Lock: 单个行记录上的锁
  • Gap Lock: 间隙锁,锁定一个范围,但不包含记录本身
  • Next-Key Lock: Gap Lock与Record Lock的结合,锁定一个范围,并且锁定记录本身

我们在分析行锁三种算法是要结合存在共享锁(S)和排他锁(X)场景,我们接着看这三种

记录锁 Record Lock

Record Lock 称为记录锁,锁住的是一条记录

sql 复制代码
SELECT * FROM `demo` WHERE `id`= 23 FOR UPDATE;

上面SQL在 id = 23 的记录上加上记录锁(X锁),这样其他事务就无法插入,更新,删除 id=23 这一行。

下面SQL是对主键索引 与 唯一索引 对数据行进行 UPDATE 操作时,也会对该行数据加记录锁:

ini 复制代码
UPDATE demo SET name = 'xiaoxu' WHERE id = 23;

记录锁是锁住记录,锁住索引记录,而不是真正的数据记录。

🚩 表中没有建索引怎么办?

即使该表上没有任何索引,那么innodb会在后台创建一个隐藏的聚集主键索引,那么锁住的就是这个隐藏的聚集主键索引。

间隙锁 GAP Lock

间隙锁 是 InnoDB 在 RR(可重复读) 隔离级别 下为了解决幻读问题时引入的锁机制。

Tips:使用间隙锁GAP Lock锁住的是一个区间,而不仅仅是这个区间中的每一条数据

sql 复制代码
SELECT * FROM demo WHERE id > 23 and id < 25 FOR UPDATE

上面语句对id范围(23, 25)的数据行加间隙锁锁,此时就无法插入id= 24的数据

临键锁 Next-Key Lock

Next-key Lock 临键锁是记录锁和间隙锁的组合,锁的范围是左开右闭区间的数据(即在某条记录以及这条记录前面间隙上的锁)。

InnoDB是使用Next-Key Lock来解决幻读问题的,在数据行上的非唯一索引列上都会存在一把临键锁。

注意:临键锁只与 非唯一索引列 有关,在 唯一索引列(包括主键列)上不存在临键锁。

上面表结构中age字段为普通索引

sql 复制代码
-- 事务A 更新age=24的记录 
UPDATE demo SET name = Vladimir WHERE age = 24;
-- 事务B 执行插入
INSERT INTO demo VALUES(100, 26, 'xiaoxu');

事务 A 在对 age 为 24 的列进行 UPDATE 操作的同时,也获取了 (24, 26] 这个区间内的临键锁,所以此时事务B会被阻塞。

问题

临键锁 Next-Key Lock如何降级?

细心的朋友会发现开头的题纲中有一个降级的指向,那么是在什么情况下发生降级的呢?

在能使用记录锁或者间隙锁就能避免幻读现象的场景下, next-key lock 就会退化成记录锁或间隙锁。

有以下场景:

唯一索引等值查询:

1.当查询的记录是存在的,next-key lock 会退化成【记录锁】 2.当查询的记录是不存在的,next-key lock 会退化成【间隙锁】

非唯一索引等值查询:

1.当查询的记录存在时,除了会加 next-key lock 外,还额外加间隙锁,也就是会加两把锁。

2.当查询的记录不存在时,只会加 next-key lock,然后会退化为间隙锁,也就是只会加一把锁。

本期关于锁的基础就讲到这里了,下期将说说MySQL行锁是如何进行加锁的!

文末小贴士!

欢迎朋友们关注我的公众号📢📢:【小许code】!🤣🤣
欢迎点赞 👍、收藏 💙、关注 💡 三连支持一下~

知道的越多,不知道的也越多,我是小许,下期见~🙇💻

相关推荐
ruleslol5 小时前
MySQL的段、区、页、行 详解
数据库·mysql
天若有情6735 小时前
校园二手交易系统实战开发全记录(vue+SpringBoot+MySQL)
vue.js·spring boot·mysql
奋进的芋圆5 小时前
DataSyncManager 详解与 Spring Boot 迁移指南
java·spring boot·后端
それども5 小时前
MySQL affectedRows 计算逻辑
数据库·mysql
是小章啊5 小时前
MySQL 之SQL 执行规则及索引详解
数据库·sql·mysql
计算机程序设计小李同学5 小时前
个人数据管理系统
java·vue.js·spring boot·后端·web安全
Echo娴6 小时前
Spring的开发步骤
java·后端·spring
追逐时光者6 小时前
TIOBE 公布 C# 是 2025 年度编程语言
后端·.net
Victor3566 小时前
Hibernate(32)什么是Hibernate的Criteria查询?
后端