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

2024年 3月 4日 58.7k 0

为什么需要引入锁

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

图片图片

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

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

MySQL锁的实现

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

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

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

图片图片

全局锁

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

flush tables with read lock(FTWRL)

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

解锁命令:

unlock tables

使用场景举例:

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

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

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

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

页锁

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

表锁

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

表锁下面又分了以下四种

图片图片

表锁

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

//加读锁
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锁): 一个事务给一个数据行加独占锁时,必须先获得表的意向独占锁

为什么需要先加意向锁?

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

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

加锁命令如下:

//加上意向共享锁,然后对读取的记录加共享锁
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锁,需要我们手动加锁。

//查询记录加共享锁
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 称为记录锁,锁住的是一条记录

SELECT * FROM `demo` WHERE `id`= 23 FOR UPDATE;

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

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

UPDATE demo SET name = 'xiaoxu' WHERE id = 23;

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

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

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

间隙锁 GAP Lock

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

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

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字段为普通索引

-- 事务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,然后会退化为间隙锁,也就是只会加一把锁。

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论