深度整理总结MySQL——MySQL加锁工作原理
MySQL加锁工作原理
- 前言
- 前置知识- 锁为什么加在索引上
- 锁的粒度优化
- 提高并发性
- 避免全表扫描
- 优化死锁处理
- 解决幻读问题
- 什么SQL语句会加行级锁
- MySQL是如何加行级锁
- 场景模拟代码
- 唯一索引等值查询
- 退化为记录锁
- 为什么会退化为记录锁
- 分析加了什么锁
- 为什么会退化为间隙锁
- 为什么我可以插入id=19的数据?
- 为什么锁住的范围是(19,21)
- 唯一索引范围查询
前言
上篇我们聊过锁都有哪些类型,那这篇我们聊MySQL什么时候会把锁添加在索引上.
顺便解释一下为什么MySQL加锁会加在索引上.
文章列举的表和数据代码都经过验证,你直接复制粘贴结果不会错(我这个数据库版本下是这样的).
前置知识- 锁为什么加在索引上
MySQL 在执行锁操作时,将锁加在索引上,而不是直接加在表的数据上,这一做法有几个重要的原因。主要是为了提高数据库操作的效率和并发性,减少锁的粒度,从而提高系统的性能。具体原因如下.
锁的粒度优化
MySQL 使用索引加锁,是为了减少锁的粒度,使得锁只作用于相关数据范围,而不是锁定整个表。通过锁定索引,MySQL 能够更精确地定位到需要操作的行,从而仅对需要的行加锁,而不是对整个表加锁。这样可以显著提升并发性能。
提高并发性
加锁索引使得多个事务可以同时在同一张表上进行不同的数据操作,而不会互相干扰。
举个例子,如果两个事务同时查询同一个表,但它们的查询条件不同,并且有索引,MySQL 就可以根据索引定位到不同的数据行,对它们分别加锁,而不需要锁定整个表,这样就能让两个事务同时执行,从而提高并发性能。
避免全表扫描
在没有索引的情况下,MySQL 需要对整个表进行扫描来查找数据,而这个过程会锁住整个表。而如果表上有索引,MySQL 就可以通过索引快速定位到目标数据,从而只锁定满足条件的行。这不仅减少了锁定的范围,还大大提高了查询性能。
优化死锁处理
由于 MySQL 将锁加在索引上,索引的有序性和结构化可以帮助 MySQL 更好地处理死锁问题。在涉及多个事务的并发操作中,通过对索引的加锁,可以确保事务按照一定的顺序进行锁定,这样可以减少死锁发生的几率。
解决幻读问题
在高并发环境下,通过加锁索引,MySQL 可以有效防止幻读现象。
通过对索引的加锁,可以确保在事务过程中,读取的数据范围是稳定的,不会因为其他事务的插入或删除操作而导致不一致。
例如,如果事务 A 查询某个范围的数据,使用了索引扫描,事务 B 插入了一些符合该范围的数据。通过索引加锁,事务 A 可以确保在整个事务期间,数据范围不被改变,从而避免幻读。
什么SQL语句会加行级锁
在说 MySQL 是怎么加行级锁的时候,是在说 InnoDB 引擎是怎么加行级锁的,因为MyISAM 引擎并不支持行级锁。
普通的 select 语句是不会对记录加锁的(除了串行化隔离级别),因为它属于快照读,是通过 MVCC(多版本并发控制)实现的。
如果要在查询时对记录加行级锁,可以使用下面这两个方式,这两种查询会加锁的语句称为锁定读.
//对读取的记录加共享锁(S型锁)
select ... lock in share mode;
//对读取的记录加独占锁(X型锁)
select ... for update;
上面这两条语句类型在使用的必须在一个事务中,因为当事务提交了,锁就会被释放,所以在使用这两条语句的时候,要加上 begin 或者 start transaction 开启事务的语句。
update 和 delete 操作都会加行级锁,且锁的类型都是独占锁(X型锁)
//对操作的记录加独占锁(X型锁)
update table .... where id = 1;
//对操作的记录加独占锁(X型锁)
delete from table where id = 1;
共享锁(S锁)满足读读共享,读写互斥。独占锁(X锁)满足写写互斥、读读互斥, 读写互斥。
共享锁仅仅共享度,独占锁什么都不共享!
MySQL是如何加行级锁
行级锁加锁规则比较复杂,不同的场景,加锁的形式是不同的。
MySQL 的行级锁是通过索引来加锁的。具体来说,MySQL 在执行 SELECT、UPDATE、DELETE 等操作时,会基于索引(主键索引、唯一索引或非唯一索引)来加锁,确保同一时刻只有一个事务对某一行数据进行修改。
但是既然是根据索引类型来加,那就是有规律的,熟悉之后也不算苦难,还是挺有意思的,有种把脑子缠住的美.
有个现象挺有意思的,行级锁上面提到有三类: Record Lock,Gap Lock(里面分纯Gap Lock和Next-key Lock).
你猜猜哪种锁是最常用的.
如果我说这三种锁实际上可以用一种锁来取代,你猜猜是哪种锁.
那肯定是Next-Key Lock.
Next-Key Lock 结合了这两种锁的功能——它既能锁住某一行(像行锁一样),又能锁住行之间的间隙(像间隙锁一样)。这样就能在很多情况下做到锁住整个查询的区域,确保没有其他事务在中间插入数据或者修改数据。
- Next-Key Lock 锁住的是 [X, X] 区间,前闭后闭的范围.这意味着它锁住了目标行的数据以及行之间的空隙,防止其他事务在这个范围内插入新数据或修改现有数据。
- Gap Lock 锁住的是 ( , ) 区间,前开后开的范围.仅仅锁住了行与行之间的空隙,防止其他事务插入数据。
- Record Lock 锁住的是 [X, X],即特定的一行数据.防止修改或删除。
那如果Next-key 像替代Gap Lock 和Record Lock的话,肯定是要有个退化的,根据不同的场景,退化为Gap Lock 或 Record Lock.
那实际上退化的场景我们大概也能想到:
- 退化为 Gap Lock:如果没有实际的数据行被锁住(比如查询的范围内没有数据),那么 Next-Key Lock 就会退化为锁住空隙,即 Gap Lock。
- 退化为 Record Lock:如果查询范围只包含单个数据行(例如精确查找某一行),那么 Next-Key Lock 会退化为 Record Lock,即仅锁住这行数据.
场景模拟代码
-- 创建表
CREATE TABLE employees (id INT PRIMARY KEY,name VARCHAR(100),salary DECIMAL(10, 2)
);-- 插入一些模拟数据
INSERT INTO employees (id, name, salary) VALUES
(1, 'Alice', 5000.00),
(2, 'Bob', 6000.00),
(3, 'Charlie', 5500.00),
(4, 'David', 7000.00),
(5, 'Eva', 6500.00),
(6, 'Frank', 7500.00);-- 创建唯一索引
CREATE UNIQUE INDEX idx_id_name ON employees(id, name);-- 创建非唯一索引
CREATE INDEX idx_salary ON employees(salary);
唯一索引等值查询
退化为记录锁
场景:
当查询的记录是「存在」的, next-key lock 会退化成「记录锁」
为什么会退化为记录锁
当我们执行唯一索引等值查询时,如果查询的记录存在,Next-Key Lock 退化为 记录锁(Record Lock)的原因,主要是因为 唯一索引查询 本身能精确定位到单一的记录。这种情况下,MySQL 只需要对该行数据加锁,而无需再加上对间隙的锁定,防止其他事务插入数据。
- 唯一索引查询的性质
唯一索引查询能精确地定位到一个具体的行。例如,当你查询某个表的主键或者一个唯一索引列时,MySQL 知道查询的结果只会有一条记录。比如:
SELECT * FROM employees WHERE id = 2 FOR UPDATE;
这条查询会根据唯一索引(假设 id 是唯一索引)定位到 id = 2 这一行数据,而 id = 2 只能有一个值。这个查询会直接锁住这一行数据。
- 没有并发插入的风险
由于唯一索引查询只能返回一个结果,所以如果查询的记录存在,MySQL 不需要担心其他事务插入数据到这个位置,因为该行数据唯一,插入不可能发生在该记录的范围内。也就是说,不存在插入“间隙”的问题。 - 该记录无法删除,因为加了记录锁,其他事务也无法删除该记录,不会出现前后两次查询的结果集不同,也避免了幻读问题.
- 不需要加Gap Lock
- 对于 唯一索引等值查询,查询的条件足够精确.只会匹配一个唯一的值(比如 id = 2)。因为这个值是唯一的,只有这一行数据存在。
- Gap Lock 锁的是行与行之间的“空白”区域,防止其他事务在该区域插入新记录。但由于 唯一索引等值查询 只涉及一条数据,MySQL 知道不会有其他事务插入数据在该位置,所以没有必要加锁该行之间的空隙区间。
分析加了什么锁
我们可以通过SELECT * FROM performance_schema.data_locks;
语句来查询事务执行SQL过程中加了什么锁.
比如上面我们执行的语句:
SELECT * FROM employees WHERE id = 2 FOR UPDATE;
我们可以在Lock_Type字段里看到一共加了两个锁,分别为:
- 表锁: X类型的意向锁
- 行锁: X类型的记录锁
在Lock_Mode 可以确认是next-key锁,间隙锁,记录锁:
- 如果 LOCK_MODE 为 X,说明是 next-key 锁;
- 如果 LOCK_MODE 为 X, REC_NOT_GAP,说明是记录锁;
- 如果 LOCK_MODE 为 X, GAP,说明是间隙锁;
为什么会退化为间隙锁
假设事务 执行了这条等值查询语句,查询的记录是「不存在」于表中的.
begin;
SELECT * FROM employees WHERE id = 20 FOR UPDATE;SELECT * FROM performance_schema.data_locks;
执行后获取的结果如下:
从上图可以看到,共加了两个锁,分别是:
- 表锁:X 类型的意向锁;
- 行锁:X 类型的间隙锁;
此时事务 在 id = 20 记录的主键索引上加的是间隙锁,锁住的范围是 (19, 21)
为什么我可以插入id=19的数据?
接下来,如果有其他事务插入19,21这一些记录的话,这些插入的雨具都会阻塞(插入id=19会被阻塞,但是插入id=9不会被阻塞).
注意如果你用本地同一个事务插入是可以成功插入的,即使你在加了间隙锁后成功插入 id = 19,并不意味着这个锁没有生效。实际上,在你插入记录时,锁住的间隙并没有阻止插入操作,因为没有其他事务在此位置竞争。间隙锁主要是防止其他事务插入新记录,而不是阻止当前事务插入。
为什么锁住的范围是(19,21)
- MySQL 根据索引的有序性来推断出加锁的区间,间隙锁的目的是防止在当前查询的区间内插入新的记录。
- 如果该查询所涉及的范围是 id = 20,但没有找到,那么 间隙锁会锁住的是 20 的位置区间,即锁住 (19, 21) 这个区间,防止其他事务插入 id = 20 或者更新 id = 19 和 id = 21 的位置。
- 这并不意味着会锁住 19 或 21 本身,而是锁住了一个范围,阻止其他事务在该范围内插入记录。
唯一索引范围查询
相关文章:

深度整理总结MySQL——MySQL加锁工作原理
MySQL加锁工作原理 前言前置知识- 锁为什么加在索引上锁的粒度优化提高并发性避免全表扫描优化死锁处理解决幻读问题 什么SQL语句会加行级锁MySQL是如何加行级锁场景模拟代码唯一索引等值查询退化为记录锁为什么会退化为记录锁分析加了什么锁为什么会退化为间隙锁为什么我可以插…...

kafka专栏解读
kafka专栏文章的编写将根据kafka架构进行编写,即先编辑kafka生产者相关的内容,再编写kafka服务端的内容(这部分是核心,内容较多,包含kafka分区管理、日志存储、延时操作、控制器、可靠性等),最后…...

1-portal认证功能
很多时候公共网络需要提供安全认证功能,比如我们去星巴克或者商场、酒店,我们连接wifi上网的时候, 需要认证后才可以上网。 用户可以主动访问已知的Portal认证网站,输入用户名和密码进行认证,这种开始Portal认证的方式…...

MySQL面试题合集
1.MySQL中的数据排序是怎么实现的? 回答重点 排序过程中,如果排序字段命中索引,则利用 索引排序。 反之,使用文件排序。 文件排序中,如果数据量少则在内存中排序, 具体是使用单路排序或者双路排序。 如果数据大则利用磁盘文件进行外部排序,一 般使用归并排序。 知识…...

spring学习(druid、c3p0的数据源对象管理)(案例学习)
目录 一、博客引言。 二、阿里云-druid案例准备(依赖坐标、配置文件、测试类)。 (1)初始依赖坐标、配置文件与测试类。 (2)导入阿里云-druid依赖坐标。 (3)DruidDataSource。 (4)set…...

WordPress博客在fnOS环境下的极简搭建与公网地址配置指南
文章目录 前言1. Docker下载源设置2. Docker下载WordPress3. Docker部署Mysql数据库4. WordPress 参数设置5. 飞牛云安装Cpolar工具6. 固定Cpolar公网地址7. 修改WordPress配置文件8. 公网域名访问WordPress 前言 你是否曾经为搭建自己的网站而头疼不已?是不是觉得…...

【PG】DROP TABLE ... CASCADE
问题 ERROR: cannot drop table wx_user_tag because other objects depend on it DETAIL: default value for column id of table wx_user_tag depends on sequence wx_user_tag_id_seq HINT: Use DROP … CASCADE to drop the dependent objects too. 解决 这个错误消息表…...

绕组电感 - Ansys Maxwell 磁通链与电流
在本博客中,我将演示如何使用 Ansys Maxwell 中磁瞬态求解器的磁通链和电流结果来计算绕组电感。Ansys Maxwell 磁瞬态求解器在场计算中考虑了涡流效应,我将展示一种使用磁通链和电流结果来计算绕组电感的简单方法。 实际上,电感是非线性的…...

物联网软件开发与应用方向应该怎样学习,学习哪些内容,就业方向是怎样?(文末领取整套学习视频,课件)物联网硬件开发与嵌入式系统
随着物联网技术的飞速发展,物联网软件开发与应用方向成为了众多开发者关注的焦点。那么,如何在这个领域中脱颖而出呢?本文将为你提供一份详细的学习指南,帮助你从零开始,逐步掌握物联网软件开发与应用的核心技能。 一…...

《LeetCode Hot100》 Day01
Day01 轮转数组 思路: (1) 使用O(1) 空间复杂度解决,就需要原地解决,不能创建新的数组。 (2) 先整体反转数组,再反转前k个数,再反转剩下的数。即可完整本题。 &…...

vue动态table 动态表头数据+动态列表数据
效果图: <template><div style"padding: 20px"><el-scrollbar><div class"scrollbar-flex-content"><div class"opt-search"><div style"width: 100px"> </div><div class"opt-b…...

1.3 GPT vs BERT 终极选择指南:从架构差异到企业级落地策略
GPT vs BERT 终极选择指南:从架构差异到企业级落地策略 引言:两大巨头的分道扬镳 2018年,BERT和GPT系列同时引爆NLP领域,却在架构选择上走向截然不同的道路: BERT采用双向Transformer Encoder,在11项NLP…...

python-leetcode 23.回文链表
题目: 给定单链表的头节点head,判断该链表是否为回文链表,如果是,返回True,否则,返回False 输入:head[1,2,2,1] 输出:true 方法一:将值复制到数组中后用双指针法 有两种常用的列表实现&#…...

食品饮料生产瓶颈?富唯智能协作机器人来 “破壁”
在食品和饮料行业的发展进程中,诸多生产瓶颈如重复性劳动负担、复杂环境作业难题、季节性产能波动等,长期制约着企业的高效运营与进一步发展。如今,富唯智能协作机器人的出现,为这些难题提供了完美的解决方案,正逐步改…...

Golang GORM系列:GORM CRUM操作实战
在数据库管理中,CRUD操作是应用程序的主干,支持数据的创建、检索、更新和删除。强大的Go对象关系映射库GORM通过抽象SQL语句的复杂性,使这些操作变得轻而易举。本文是掌握使用GORM进行CRUD操作的全面指南,提供了在Go应用程序中有效…...

C++ labmbd表达式
文章目录 C++ Lambda 表达式详解1. Lambda 表达式的组成部分:2. Lambda 语法示例(1) 最简单的 Lambda(2) 带参数的 Lambda(3) 指定返回类型的 Lambda3. 捕获外部变量(1) 值捕获(复制)(2) 引用捕获(3) 捕获所有变量4. Lambda 在 STL 中的应用5. Lambda 作为 `std::function`6…...

《大规模动画优化(一):GPU 顶点动画的生成》
GPU 顶点动画(Vertex Animation Texture, VAT) GPU 顶点动画(Vertex Animation Texture, VAT)烘焙的核心思想是: 在 CPU 端预先计算动画顶点数据,并存储到纹理(Texture2D)中…...

【前端】几种常见的跨域解决方案
在前端开发中,跨域问题是常见的挑战。以下是几种常见的跨域解决方案: 1. Nginx反向代理 使用 Nginx 进行反向代理是解决跨域问题的一种常见方式。Nginx 会充当一个中间代理服务器,接收来自前端的请求并将其转发到实际的后端 API 服务&#…...

如何在WinForms应用程序中读取和写入App.config文件
如何在WinForms应用程序中读取和写入App.config文件 1. 添加App.config文件2. 配置App.config3. 读取App.config4. 写入App.config 在WinForms应用程序中, App.config文件是用于存储配置数据的标准方式。通过使用.NET框架提供的类库,我们可以方便地对 …...

【分布式理论7】分布式调用之:服务间的(RPC)远程调用
文章目录 一、RPC 调用过程二、RPC 动态代理:屏蔽远程通讯细节1. 动态代理示例2. 如何将动态代理应用于 RPC 三、RPC序列化与协议编码1. RPC 序列化2. RPC 协议编码2.1. 协议编码的作用2.2. RPC 协议消息组成 四、RPC 网络传输1. 网络传输流程2. 关键优化点 一、RPC…...

人工智能应用-智能驾驶精确的目标检测和更高级的路径规划
实现更精确的目标检测和更高级的路径规划策略是自动驾驶领域的核心任务。以下是一个简化的示例,展示如何使用Python和常见的AI库(如TensorFlow、OpenCV和A*算法)来实现这些功能。 1. 环境准备 首先,确保安装了以下库:…...

dynamic_cast和static_cast和const_cast
dynamic_cast 在 C 中的作用 dynamic_cast 是 C 运行时类型转换(RTTI, Run-Time Type Identification)的一部分,主要用于: 安全的多态类型转换检查类型的有效性向下转换(Downcasting)跨类层次的指针或引用…...

DEEPSEEK与GPT等AI技术在机床数据采集与数字化转型中的应用与影响
随着人工智能(AI)技术的迅猛发展,深度学习、自然语言处理等先进技术开始广泛应用于各行各业。在制造业尤其是机床行业,AI技术的融合带来了巨大的变革,尤其在机床数据采集与机床数字化方面的应用。本文将探讨DEEPSEEK、…...

高速存储文章目录
《zynq tcp万兆网和ftp协议分析-CSDN博客》 《国产fpga nvme ip高速存储方案设计_fpga 高速存储-CSDN博客》 《国微pcie switch 8748高速存储方案设计_国产pcie switch-CSDN博客》 《FPGA SATA高速存储设计-CSDN博客》 《FPGA NVME高速存储设计_690t fpga-CSDN博客》 《zy…...

车载测试工具 --- CANoe VH6501 进行Not Acknowledge (NAck) 测试
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活,除了生存温饱问题之外,没有什么过多的欲望,表面看起来很高冷,内心热情,如果你身…...

【清晰教程】通过Docker为本地DeepSeek-r1部署WebUI界面
【清晰教程】本地部署DeepSeek-r1模型-CSDN博客 目录 安装Docker 配置&检查 Open WebUI 部署Open WebUI 安装Docker 完成本地DeepSeek-r1的部署后【清晰教程】本地部署DeepSeek-r1模型-CSDN博客,通过Docker为本地DeepSeek-r1部署WebUI界面。 访问Docker官…...

Linux运维——用户管理
Linux用户管理 一、Linux用户管理要点二、常用命令2.1、groupadd2.2、groupdel2.3、groupmod2.4、groups2.5、useradd2.6、userdel2.7、passwd2.9、su2.10、sudo2.10.1、给普通用户授权 sudo2.10.2、 免密码授权 sudo 一、Linux用户管理要点 创建用户组 - 使用 groupadd删除用…...

mac下dify+deepseek部署,实现私人知识库
目前deepseek 十分火爆,本地部署实现私有知识库,帮助自己日常工作,上一篇使用工具cherry studio可以做到私人知识库。今天学习了一下,使用Dify链接deepseek,实现私人知识库,也非常不错,这里分享…...

Linux中设置开机运行指令
系统:Debian 12 使用systemd来设置开机自启动脚本或命令是一个更加现代且推荐的方法。下面是具体的步骤: 创建守护脚本 首先,你需要创建一个Shell脚本文件,比如mydaemon.sh,并在其中编写你的守护脚本逻辑。确保这个脚…...

IDEA中列举的是否是SpringBoot的依赖项的全部?在哪里能查到所有依赖项,如何开发自己的依赖项让别人使用
在 IntelliJ IDEA 中列举的依赖项并不一定是 Spring Boot 项目的全部依赖项。IDEA 通常只显示你在 pom.xml(Maven)或 build.gradle(Gradle)中显式声明的依赖项,而这些依赖项本身可能还会引入其他传递性依赖。 1. 如何…...