拒绝零散碎片, 一文理清MySQL的各种锁

系列文章目录

学习MySQL先有全局观,细说其发展历程及特点
Mysql常用操作,谈谈排序与分页


在这里插入图片描述

相信大家在日常使用Mysql的过程中,对锁也有着一些了解。什么排它/共享锁,表锁、行锁,还有意向锁之类的。相信同学们也看到过很多相关文章,不过大部分文章只是偏重其中某一种锁进行深入分析,实话实说,确实有不少锁的逻辑较为复杂,值得摸索。但对于大部分同学而言,我认为最好是先对这些锁有个总体的认知,做到先总再分,这样才更能从全局了解锁的设计。

📕作者简介:战斧,从事金融IT行业,有着多年一线开发、架构经验;爱好广泛,乐于分享,致力于创作更多高质量内容
📗本文收录于 mysql 专栏,有需要者,可直接订阅专栏实时获取更新
📘高质量专栏 云原生、RabbitMQ、Spring全家桶 等仍在更新,欢迎指导
📙Zookeeper Redis dubbo docker netty等诸多框架,以及架构与分布式专题即将上线,敬请期待

一、MySQL的锁指什么

我们先来看一下Mysql的基本构成,如下图,以前我们其实也介绍过,MySQL数据库总体分为三个部分:

  • Server层:负责处理客户端连接、查询解析和优化、数据访问控制、事务处理、日志、replication和其他管理操作。
  • 存储引擎:负责数据的存储和检索等操作。MySQL支持多个存储引擎,如InnoDB、MyISAM、MEMORY等。
  • 物理磁盘层:真正存储数据的位置,保存着数据库数据以及各类日志。

在这里插入图片描述

我们日常说的MySQL的各种锁,有的是由存储引擎提供的(如行锁),有些锁是有Server层提供的(如全局锁、表锁),它们结合起来才形成了完整的锁的体系

二、排他与共享

锁从不同角度理解,其实会有不同的分类,也就是说同一个东西,在不同的分类下叫不同的锁,但其实它们并不冲突。在讨论 MySQL 的锁时,一般我们会有这样两种分类:

  • 锁的粒度
  • 是否排他

从锁的粒度来看,可以分为全局锁表锁行锁,顾名思义,锁的层级分别是数据库、某张表、以及表里的行。
从是否独占来看,可以分为排它锁共享锁

在innoDB,对于同一个资源,是允许设定 排它锁(X) 和 共享锁(S) 两种的,它们的兼容关系如下:

XS
X冲突冲突
S冲突兼容

比如事务A对某一行上了共享锁(S),此时再来一个事务B,如果B需要这个资源的共享锁,那么它能立即获得。如果B需要该资源的排它锁,事务B就需要等待了。

而不同粒度的锁,接下来我们以一一讲解

三、全局锁(Global Lock)

用于 限制整个数据库实例 的访问,当执行一些需要全局一致性的操作时,例如备份、恢复等,可以使用全局锁,如下命令

 FLUSH TABLES WITH READ LOCK

这个命令会对所有数据库的所有表都上一个读锁,加完这个锁后,所有的表都会被锁定从而无法插入任何内容了(mysql自己的系统日志表不在此列)

四、表锁(Table Lock)

锁定整个表,在执行涉及整个表的操作时,会对整个表进行锁定,避免其他事务对该表进行并发操作。它的上锁及解锁语法如下

-- 加锁
LOCK {TABLE | TABLES}tbl_name [[AS] alias] lock_type[, tbl_name [[AS] alias] lock_type] ...
lock_type: {READ [LOCAL]| WRITE
}-- 释放锁
UNLOCK {TABLE | TABLES}

一般情况下,不建议使用这种表锁,除非是对一组MyISAM的表来操作,那么提前将它们锁定会提高效率

五、意向锁(Intention Locks)

InnoDB 支持多粒度锁,允许行锁和表锁共存。例如下面的语句就可以在指定的表上使用排他锁(X锁):

LOCK TABLES … WRITE

而为了同时避免遍历行锁的困境:比如事务A获取了某一行的排它锁,此时事务B想锁表,就必须遍历每一行检查是否有锁;同时也为了避免不同粒度间的锁出现死锁,所以InnoDB使用了意向锁。意向锁是表级别的锁,它指示事务以后对表中的某一行需要哪种类型的锁(共享的还是排他的)。有两种类型的意向锁:

  • 意向共享锁(IS)表明事务打算对表中的单个行设置共享锁
  • 意向排他锁(IX)表示事务打算对表中的单个行设置排他锁

例如,SELECT…FOR SHARE 设置IS锁,SELECT…FOR UPDATE 设置一个IX锁。
在事务获得表中某一行的共享锁之前,它必须首先获得表上的IS锁或更强的锁。
在事务获得表中某一行的排他锁之前,它必须首先获得该表上的IX锁

表级锁类型兼容性如下表所示:

XIXSIS
X冲突冲突冲突冲突
IX冲突兼容冲突兼容
S冲突冲突兼容兼容
IS冲突兼容兼容兼容

这个表的理解其实很简单:
(1)X 作为表的排他锁,和其他所有表锁冲突
(2)IX 代表准备修改某一行,此时如果有人持有表,不论是 X 还是 S,为了防止冲突或不一致,所以会返回冲突

如果锁与现有锁兼容,则将锁授予请求事务,但如果它与现有锁冲突,则不会授予。事务等待,直到冲突的现有锁被释放。如果锁请求与现有锁冲突,并且由于会导致死锁而无法授予,则会发生错误。

意向锁的主要目的是显示某人正在锁定表中的一行,或者将要锁定表中的一行,作为行锁前置的隐式锁。也就是说若你仅使用表锁,或仅使用行锁,意向锁是不会让你阻塞的。只有你在持有行锁的情况下又使用表锁,它才能发挥它的用处

六、行级锁(Row Lock)

在InnoDB存储引擎中,默认使用的是行级锁。它可以实现更细粒度的并发控制,只锁定部分行,而不是整个表,提高了并发性能。而从实际表现来说,其又分为三种 记录锁(Record Locks)间隙锁(Gap Locks),以及所谓临键锁(Next-Key Locks)。实际上在源码种,这三种锁其实叫 LOCK_REC_NOT_GAPLOCK_GAPLOCK_ORDINARY

/* Precise modes /
/
* this flag denotes an ordinary next-key lock in contrast to LOCK_GAP or
LOCK_REC_NOT_GAP /
constexpr uint32_t LOCK_ORDINARY = 0;
/
* when this bit is set, it means that the lock holds only on the gap before
the record; for instance, an x-lock on the gap does not give permission to
modify the record on which the bit is set; locks of this type are created
when records are removed from the index chain of records /
constexpr uint32_t LOCK_GAP = 512;
/
* this bit means that the lock is only on the index record and does NOT
block inserts to the gap before the index record; this is used in the case
when we retrieve a record with a unique key, and is also used in locking
plain SELECTs (not part of UPDATE or DELETE) when the user has set the READ
COMMITTED isolation level */
constexpr uint32_t LOCK_REC_NOT_GAP = 1024;

1. 记录锁(Record Locks)

记录锁是索引记录上的锁,例如,

SELECT c1 FROM t WHERE c1=10 For update;

这样就能阻止任何其他事务插入、更新或删除c1是10为的行。
记录锁总是锁定索引,即使定义的表没有索引也是如此。对于这样的情况,InnoDB创建一个隐藏的聚集索引,并使用该索引进行记录锁定

2. 间隙锁(Gap Locks)

如果我们把隔离级别设定为可重复度(RR),MySQL就会为我们解决”幻读“现象,这里面除了MVCC的作用外,还会在此时引入”间隙锁“的概念,间隙锁本质上是 在索引记录之间的间隙上的锁,或者在第一个索引记录之前或最后一个索引记录之后的间隙上的锁。比如说当我们执行这样一个SQL时

SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 For Update

就会防止其他事务将值15插入到列t.c1中,因为范围中所有现有值之间的间隙被锁定。

3. 临键锁(Next-Key Locks)

next-key锁是索引记录上的记录锁和索引记录之前的间隙上的间隙锁的组合。InnoDB执行行级锁的方式是,当它搜索或扫描一个表索引时,它会在遇到的索引记录上设置共享锁或排他锁。因此,行级锁实际上是索引记录锁。索引记录上的next-key锁也会影响该索引记录之前的“间隙”。也就是说,next-key锁是索引记录锁加上索引记录前面的间隙锁

假设索引包含值10、11、13和20。此索引的Next-Key Locks锁定则覆盖以下区间,其中圆括号表示不包含区间端点,方括号表示包含端点:

(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)

对于最后一个间隔,next-key锁锁定索引中最大值以上的间隙,以及值高于索引中任何实际值的“supremum”伪记录。”supremum“不是一个真正的索引记录,因此,实际上,这个next-key锁只锁定最大索引值后面的间隙。默认情况下,InnoDB运行在REPEATABLE READ事务隔离级别。在这种情况下,InnoDB使用next-key锁进行搜索和索引扫描,这可以防止幻行。

4. 插入意向锁(Insert Intention Locks)

学习了上面的间隙锁,我们不难知晓,想要插入新数据通常需要先获得间隙锁(隔离级别为可重复读及以上)。按照常理来说,如果有多个事务都想往一个间隙里插入数据,它们只要慢慢排队就好了,但是,我们还需要意识到,一旦事务A插入一条数据成功,原先的间隙可能就会变成两个间隙,事务B可能又被A新诞生的这个间隙所阻碍。为了优化这种插入排队的情况,innodb提出了插入意向锁的概念:

An insert intention lock is a type of gap lock set by INSERT operations prior to row insertion. This lock
signals the intent to insert in such a way that multiple transactions inserting into the same index gap
need not wait for each other if they are not inserting at the same position within the gap

插入意图锁是insert操作在行插入之前设置的一种间隙锁。这个锁以这样一种方式表示插入的意图,即插入到相同索引间隙中的多个事务如果不在间隙内的相同位置插入,则不需要彼此等待

也就是说,插入操作在获取间隙锁之前,会先获取到插入意向锁,下面的示例演示了一个事务在获得插入记录的排他锁之前使用插入意图锁。该示例涉及两个客户机A和b。客户机A创建一个包含两个索引记录(90和102)的表,然后启动一个事务,对ID大于100的索引记录设置排他锁。排他锁包括记录102之前的间隙锁:

mysql> CREATE TABLE child (id int(11) NOT NULL, PRIMARY KEY(id)) ENGINE=InnoDB;
mysql> INSERT INTO child (id) values (90),(102);mysql> START TRANSACTION;
mysql> SELECT * FROM child WHERE id > 100 FOR UPDATE;
+-----+
| id |
+-----+
| 102 |
+-----+

客户端B开始一个事务,向缺口间隙插入一条记录,事务在等待获得排他锁时接受插入意图锁:

mysql> START TRANSACTION;
mysql> INSERT INTO child (id) VALUES (101);

在SHOW ENGINE INNODB STATUS和INNODB monitor输出中,插入意图锁的事务数据如下所示:

RECORD LOCKS space id 31 page no 3 n bits 72 index `PRIMARY` of table `test`.`child`
trx id 8731 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 00: len 4; hex 80000066; asc f;;1: len 6; hex 000000002215; asc " ;;2: len 7; hex 9000000172011c; asc r ;;...

5. 行锁总结

其实对于行锁,现在解析的文章有很多,我们后续也可能结合源码进行更深入的解释。但在这里我们可以先进行总结(源于Mysql源码版本8.0.30)

  • 锁基于扫描到的索引,对于innoDB,哪怕你没有显示建立索引,每一张表也都至少会有一个索引(即其聚集索引
  • 间隙锁(Gap Locks) 只有在隔离级别为可重复读以上才会有
  • 隔离级别为读未提交/读提交时,行级锁默认使用记录锁(Record Locks),隔离级别为可重复读/序列化时,行级锁默认使用的是临键锁(Next-Key Locks)
  • 间隙锁(Gap Locks) 的目的是不让这段间隙内插入新的数据,因此其设计与传统意义上的共享/排他概念不一样。比如:事务A在一个间隙上持有共享间隙锁(间隙s锁),而事务B可以在同一个间隙上持有排他性间隙锁(间隙x锁)

七、自增锁(AUTO-INC Locks)

AUTO-INC锁是一种特殊的表级锁,用于在具有AUTO_INCREMENT列的表中插入事务。

在最简单的情况下,如果一个事务正在向表中插入值,那么任何其他事务都必须等待对该表进行自己的插入,以便第一个事务插入的行接收连续的主键值。innodb_autoinc_lock_mode变量控制用于自动增量锁定的算法。它允许您选择如何在可预测的自动递增值序列和插入操作的最大并发性之间进行权衡。

在这里插入图片描述
这里有 0、1、2 三种模式可选

  • 0(traditional):这是最常用的模式。在这种模式下,InnoDB使用一个全局的互斥锁(AUTO-INC锁)来保护自增主键的访问。当有一个事务插入新记录时,其他事务必须等待该事务释放AUTO-INC锁后才能插入新记录,该锁通常保持到语句结束(而不是事务结束)。

  • 1(consecutive):在这种模式下,InnoDB使用一个递增的互斥锁(AUTO-INC锁)来保护自增主键的访问。如果我们可以预知插入的数据条数,InnoDB会为每个事务分配一个独立的自增值区间。当一个事务需要插入自增值时,它就可以在自己的区间内找到一个可用的自增值,然后将其插入到表中。通过为每个事务分配独立的自增值区间,Consecutive模式可以实现并行插入,这种模式适用于具有高并发读写的应用,可以提高性能。

  • 2(interleaved):这种模式是在MySQL 8.0中引入的新模式。在这种模式下,事务们都不再持有表级AUTO-INC锁,该模式可以支持多个语句可以同时生成数字,也就是说,数字的分配在多个语句之间交错进行。

需要注意的是,在我们使用它不同种类的插入语句的时候,consecutive 自增锁有可能还是会使用全局互斥锁的样子,因为不是每一种 insert 都能提前预知其数量的,比如下面这样的插入:

INSERT INTO t1 (c2) SELECT ... from another table ...

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/355918.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java网络爬虫入门

文章目录 1、导入依赖2、CrawlerFirst 1、导入依赖 <dependencies><!-- HttpClient --><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.3</version></…

【ARMv8/ARMv9 硬件加速系列 3.3 -- SVE LD2D 和 ST2D 使用介绍】

文章目录 SVE 多向量操作LD2D(加载)LD2D 操作说明LD2D 使用举例ST2D(存储)ST2D 使用举例ST2D 存储示例代码ld2d 和 st2d 小结SVE 多向量操作 在ARMv8/9的SVE (Scalable Vector Extension) 指令集中,st2d和ld2d指令用于向量化的存储和加载操作,具体地,它们允许同时对两个…

英文字母表

目录 一 设计原型 二 后台源码 一 设计原型 二 后台源码 namespace 英文字母表 {public partial class Form1 : Form{public Form1(){InitializeComponent();}private void Form1_Load(object sender, EventArgs e){foreach (var item in panel1.Controls){if (item ! null)…

代理网络基础设施 101:增强安全性、速度和可扩展性

编辑代理网络在现代网络架构中发挥着重要作用&#xff0c;充当管理和重新路由数据流的中介。它们处理的数据可以是各种类型&#xff0c;包括搜索查询和潜在的敏感客户信息&#xff0c;这凸显了它们在数据安全方面的作用。 然而&#xff0c;代理的好处不仅限于安全性。它们为用…

java:Multiple Bounds--类型变量(TypeVariable)定义的高级用法--<A extends ClassAIfAIfB >

做Java开发工作好多年了。今天偶然翻到 java.lang.TypeVariable的源码&#xff0c;好奇为什么 TypeVariable.getBounds()返回类型是个数组。 一般不都是<T extends Number> 这样用码&#xff1f;T难道还能extends多个类型&#xff1f; 同问&#xff1a;不应该是extend,为…

Java基础 - 练习(五)根据今天日期获取一周内的日期(基姆拉尔森公式)

基姆拉尔森计算公式用于计算一周内的日期。比如给你年月日&#xff0c;从而计算今天是星期几。 基姆拉尔森公式 Week (d2*m3*(m1)/5yy/4-y/100y/4001) mod 7&#xff0c; 3<m<14Week的取值范围是0 ~ 6&#xff0c;其中0代表星期日&#xff0c;1 ~ 6分别代表星期一到星期…

如何使用Windows备份轻松将数据转移到新电脑?这里有详细步骤

序言 我们都知道那种买了一台新电脑,就想直接上手的感觉。我记得在过去的日子里,要花几个小时传输我的文件,并试图复制我的设置。在当今传输数据的众多方法中,Windows备份提供了一个简单可靠的解决方案。 登录到你的Microsoft帐户 Microsoft在传输过程中使用其云存储来保…

C# WPF入门学习主线篇(二十三)—— 控件模板(ControlTemplate)和数据模板(DataTemplate)

C# WPF入门学习主线篇&#xff08;二十三&#xff09;—— 控件模板&#xff08;ControlTemplate&#xff09;和数据模板&#xff08;DataTemplate&#xff09; 在WPF开发中&#xff0c;控件模板&#xff08;ControlTemplate&#xff09;和数据模板&#xff08;DataTemplate&am…

设置浏览器互不干扰

目录 一、查看浏览器文件路径 二、 其他盘新建文件夹Cache 三、以管理员运行CMD 四、执行命令 一、查看浏览器文件路径 chrome://version/ 二、 其他盘新建文件夹Cache D:\chrome\Cache 三、以管理员运行CMD 四、执行命令 Mklink /d "C:\Users\Lenovo\AppData\Loca…

社区项目-项目介绍环境搭建

文章目录 1.技术选型2.原型设计1.安装AxureRP2.进行汉化3.载入元件库4.基本设计 3.元数建模1.安装元数建模软件2.新建项目3.新增一个刷题模块主题域4.新增数据表 subject_category5.新增关系图&#xff0c;将表拖过来6.新增题目标签表7.新增题目信息表8.新增单选表、多选表、判…

【linux】dup文件描述符复制函数和管道详解

目录 一、文件描述符复制 1、dup函数&#xff08;复制文件描述符&#xff09; ​编辑 2、dup2函数&#xff08;复制文件描述符&#xff09; ​编辑 二、无名管道pipe 1、概述 2、无名管道的创建 3、无名管道读写的特点 4、无名管道ps -A | grep bash实现 三、有名管道FI…

深度学习Week17——优化器对比实验

文章目录 深度学习Week17——优化器对比实验 一、前言 二、我的环境 三、前期工作 1、配置环境 2、导入数据 2.1 加载数据 2.2 检查数据 2.3 配置数据集 2.4 数据可视化 四、构建模型 五、训练模型 1、将其嵌入model中 2、在Dataset数据集中进行数据增强 六、模型评估 1、Accur…

让我来告诉初学者到底什么叫嵌入式系统?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「嵌入式的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;我们在刚刚开始学习电子学…

噪声-降噪引脚如何提高系统性能

由于LDO是电子器件&#xff0c;因此它们会自行产生一定量的噪声。选择低噪声LDO并采取措施来降低内部噪声对于生成不会影响系统性能的清洁电源轨而言不可或缺。 识别噪声 理想的 LDO 会生成没有交流元件的电压轨。遗憾的是&#xff0c;LDO 会像其他电子器件一样自行产生噪声。…

Java数据类型与运算符

1. 变量和类型 变量指的是程序运行时可变的量&#xff0c;相当于开辟一块空间来保存一些数据。 类型则是对变量的种类进行了划分&#xff0c;不同类型的变量具有不同的特性。 1.1 整型变量&#xff08;重点&#xff09; 基本语法格式&#xff1a; int 变量名 初始值;代码示…

大语言模型-Transformer

目录 1.概述 2.作用 3.诞生背景 4.历史版本 5.优缺点 5.1.优点 5.2.缺点 6.如何使用 7.应用场景 7.1.十大应用场景 7.2.聊天机器人 8.Python示例 9.总结 1.概述 大语言模型-Transformer是一种基于自注意力机制&#xff08;self-attention&#xff09;的深度学习…

开发中遇到的错误 - @SpringBootTest 注解爆红

我在使用 SpringBootTest 注解的时候爆红了&#xff0c;ait 回车也导不了包&#xff0c;后面发现是因为没有加依赖&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId>…

云计算技术高速发展,优势凸显

云计算是一种分布式计算技术&#xff0c;其特点是通过网络“云”将巨大的数据计算处理程序分解成无数个小程序&#xff0c;并通过多部服务器组成的系统进行处理和分析这些小程序&#xff0c;最后将结果返回给用户。它融合了分布式计算、效用计算、负载均衡、并行计算、网络存储…

MEME使用-motif分析(生物信息学工具-24)

01 背景 Motif分析是一种在生物信息学和计算生物学中广泛应用的技术&#xff0c;用于识别DNA、RNA或蛋白质序列中具有生物学功能的短保守序列模式&#xff08;motif&#xff09;。这些motif通常与特定的生物学功能相关&#xff0c;如DNA中的转录因子结合位点、RNA中的剪接位点…

C++ 计算凸包点的最小旋转矩形

RotateRect.h #include <vector>/** * brief 计算点集最小旋转外接矩形 */ class RotateRect { public:enum { CALIPERS_MAXHEIGHT 0, CALIPERS_MINAREARECT 1, CALIPERS_MAXDIST 2 };struct Point {float x, y;};using Points std::vector<Point>;struct Size…