什么是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】!🤣🤣
欢迎点赞 👍、收藏 💙、关注 💡 三连支持一下~

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

相关推荐
C吴新科1 小时前
MySQL入门操作详解
mysql
NiNg_1_2341 小时前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
Chrikk2 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*2 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue2 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man2 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
Ai 编码助手3 小时前
MySQL中distinct与group by之间的性能进行比较
数据库·mysql
白云如幻4 小时前
MySQL排序查询
数据库·mysql
苹果醋34 小时前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx
customer084 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源