一篇文章看懂mysql加锁

一篇文章看懂Mysql加锁

本文主要基于Mysql8,InnoDB存储引擎范围讨论mysql的加锁,以及锁的分类,定义,使用,不同语句具体加的什么锁等。

前言

mysql锁是和事务绑定的。本文介绍了了:全局锁、表锁、行锁、MDL锁、Auto_inc 锁。插入意向锁(Insert Intention gap Locks)

  • **全局锁:**锁整个数据库,主要用于全库备份
  • **表锁:**存储引擎提供的 S 锁和 X 锁 (一般不用);server 层提供的 MDL 锁;意向 S 锁,意向 X 锁;用于主键自增的 AUTO-INC 锁
  • **行锁:**Record Lock (记录锁);Gap Lock (间隙锁);Next-Key Lock (记录锁 + 间隙锁)
  • MDL锁。元数据锁 (Metadata Lock,MDL) 是 server 层提供,不需要显示使用 MDL 锁,因为会根据操作自动为表添加 MDL。
  • AUTO-INC锁是一种特殊的表级锁,发生涉及AUTO_INCREMENT列的事务性插入操作时产生。
  • insert intention gap lock锁是插入的时候产生的。

全局锁

flush tables with read lock

执行后,整个数据库就处于只读状态了,这时其他线程执行以下操作,都会被阻塞:全局锁主要应用于做全库逻辑备份,这样在备份数据库期间,不会因为数据或表结构的更新,而出现备份文件的数据与预期的不一样。

表级锁

加锁语句;

//表级别的共享锁,也就是读锁;
lock tables table1 read;//表级别的独占锁,也就是写锁;
lock tables table1 write;

行级锁

行锁有2种类型,X(exclusive)锁 和S(share)锁。排斥锁和共享锁。

X 锁(Exclusive Locks)和 S 锁(Shared Locks)是数据库中两种基本的锁类型,它们在并发访问数据时用于保护数据的一致性和完整性。以下是它们的主要区别:

  1. 互斥性
    • X 锁是排他性的,也称为写锁。当一个事务获取了某个资源的 X 锁后,其他任何事务都无法再对这个资源加锁,无论是读还是写操作都会被阻塞。
    • S 锁是非排他性的,也称为读锁。多个事务可以同时获取同一个资源的 S 锁,但不能同时获取 X 锁。
  2. 并发性
    • X 锁限制了并发性,因为它不允许其他事务在同一时间内对同一资源进行读取或修改。
    • S 锁允许一定程度的并发性,因为多个事务可以同时读取相同的数据。
  3. 锁定的目的
    • X 锁主要用于更新操作,确保在更新期间不会发生冲突。
    • S 锁主要用于查询操作,保证在读取期间数据不被改变。
  4. 兼容性
    • 一个资源上的 X 锁与任何其他类型的锁都不兼容,包括其他的 X 锁和 S 锁。
    • 一个资源上的 S 锁与另一个 S 锁是兼容的,但与 X 锁不兼容。
  5. 用途
    • 在事务处理过程中,为了防止脏读、不可重复读和幻读等并发问题,通常会根据不同的隔离级别使用相应的锁策略。
    • X 锁通常在需要更新数据的时候使用,以避免并发的写冲突。
    • S 锁通常在只读操作或者乐观并发控制(如版本控制)的情况下使用,以提高并发性能。
  6. 解锁顺序
    • 当一个事务释放其持有的 X 锁时,其他等待该锁的事务将有机会获得它。
    • 对于 S 锁,当一个事务释放其持有的 S 锁时,其他等待 S 锁的事务也可以立即获得它,但如果有事务正在等待 X 锁,则仍然需要等到所有 S 锁都被释放。

这些差异使得 X 锁和 S 锁在数据库并发控制中有不同的应用场景和作用。通过合理地使用这两种锁,可以有效地管理并发事务,避免数据冲突并保持数据的一致性。

意向锁(intention locks)

意向锁是表级别锁。

主要分为二种IS(intention shared lock) 和 IX(intention exclusive lock);

用于管理行级的S锁(Shared Locks)和X锁(Exclusive Locks)。

  1. IS 锁
    • IS 锁表示一个事务想要在一个记录上加 S 锁。
    • 当一个事务开始在某一行上获取 S 锁之前,它会先在该行所在的表上加一个 IS 锁。
    • 如果多个事务都试图在相同的行上获取 S 锁,那么他们都会先获得 IS 锁,然后竞争同一行上的 S 锁。
    • IS 锁本身不会阻塞其他事务对相同表进行读取操作,即不会阻止其他事务获取 IS 锁或者S锁。
    • 加锁方式:select … lock in share mode
  2. IX 锁
    • IX 锁表示一个事务想要在一个记录上加 X 锁。
    • 当一个事务开始在某一行上获取 X 锁之前,它会先在该行所在的表上加一个 IX 锁。
    • 如果多个事务都试图在相同的行上获取 X 锁,那么他们都会先获得 IX 锁,然后竞争同一行上的 X 锁。
    • IX 锁会阻塞其他事务在同一表上获取 S 锁或 X 锁,但不会阻止其他事务获取 IS 锁。
    • select …. for update

简单来说,IS 和 IX 锁的作用是为了在多个事务尝试获取同一行的 S 或 X 锁时提供一种协调机制。当一个事务需要访问特定行时,它首先会在表级别获取一个 IS 或 IX 锁,然后再去获取行级别的 S 或 X 锁。这样可以防止并发冲突并确保数据的一致性。

不同锁的兼容性

XIXSIS
XConflictConflictConflictConflict
IXConflictCompatibleConflictCompatible
SConflictConflictCompatibleCompatible
ISConflictCompatibleCompatibleCompatible

!

横向:已经持有的锁。纵向:正在请求的锁。

分析兼容矩阵可以得出以下结论:

  • INSERT操作之间不会有冲突。
  • GAP,Next-Key会阻止Insert。
  • GAP和Record,Next-Key不会冲突
  • Record和Record、Next-Key之间相互冲突。
  • 已有的Insert锁不阻止任何准备加的锁。

其他三种类型行级锁(重点)

行级锁主要有三种类型:

  • **Record Lock:**记录锁,锁定一条记录
  • **Gap Lock:**间隙锁,锁定一个范围,但不包含记录本身。数学符号描述(a,b)。左开右开区间。
    间隙锁用于锁定索引记录之间的空隙,防止在该范围内插入新的记录。
    当执行范围查询时,InnoDB 会自动为搜索范围内的所有间隙加锁。
  • **Next-Key Lock:**Record Lock 和 Gap Lock 的结合,既锁定记录,也锁定范围 左开右闭区间。(a,b]

加锁的一些原则:

  1. 加锁的基本单位是 next-key lock。左开右闭。
  2. 索引上的等值查询,给唯一索引加锁的时候,如果存在next-key lock 退化为 record Lock。(重要,更新操作,尽可能使用唯一索引进行更新)。
  3. 查找过程中访问到的对象才会加锁。
  4. 索引上的等值查询,向右遍历时,且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁。

加锁具体分析:

# 下面两条 select 和普通的 select 的区别在于:
#  - 普通 select 是快照读,不会加锁
#  - 下面的两条 select 是当前读,永远都读最新的数据,会加锁
select ... lock in share mode;  # 对读取的记录加共享锁 (S 锁)
select ... for update           # 对读取的记录加独占锁 (X 锁)# update 和 delete 每次也都是读取最新的数据,然后执行修改或删除操作,且都会加锁
update ...                      # 对更新的记录加独占锁 (X 锁)
delete ...                      # 对删除的记录加独占锁 (X 锁)

假设有这么一张 user 表,id 为主键(唯一索引),a 是普通索引(非唯一索引),b 都是普通的列,其上没有任何索引:

id (唯一索引)a (非唯一索引)b
104Alice
158Bob
2016Cilly
2532Druid
3064Erik

案例 1:唯一索引等值查询

当我们用唯一索引进行等值查询的时候,根据查询的记录是否存在,加锁的规则会有所不同:

  1. 当查询的记录是存在的,Next-key Lock 会退化成记录锁
  2. 当查询的记录是不存在的,Next-key Lock 会退化成间隙锁

查询的记录存在

事务A
begin:
select * from user where id = 25
for update;
commit;

结合加锁的两条核心:查找过程中访问到的对象才会加锁 + 加锁的基本单位是 Next-key Lock(左开右闭),我们可以分析出,这条语句的加锁范围是 (20, 25]

不过,由于这个唯一索引等值查询的记录 id = 25 是存在的,因此,Next-key Lock 会退化成记录锁,因此最终的加锁范围是 id = 25 这一行。

X锁记录锁和其他任何锁,都不兼容。事务A没有提交期间,其他事务执行获取id=25的记录锁的都会被阻塞。(比如 select * from user where id =25 in share mode; update …… where id = 25; delete … where id = 25);

查询的记录不存在

再来看查询的记录不存在的案例:

select * from user where id = 22
for update;

innodb 先找到 id = 20 的记录,发现不匹配,于是继续往下找,发现 id = 25,因此,id = 25 的这一行被扫描到了,所以整体的加锁范围是 (20, 25]

由于这个唯一索引等值查询的记录 id = 22 是不存在的,因此,Next-key Lock 会退化成间隙锁,因此最终在主键 id 上的加锁范围是 Gap Lock (20, 25)

案例 2:唯一索引范围查询

  1. 唯一索引范围查询的规则和等值查询的规则一样,只有一个区别,就是唯一索引的范围查询需要一直向右遍历到第一个不满足条件的记录,下面结合案例来分析:
select * from user where id >= 20 and id < 22
for update;

先来看语句查询条件的前半部分 id >= 20,因此,这条语句最开始要找的第一行是 id = 20,结合加锁的两个核心,需要加上 Next-key Lock (15,20]。又由于 id 是唯一索引,且 id = 20 的这行记录是存在的,因此会退化成记录锁,也就是只会对 id = 20 这一行加锁。

再来看语句查询条件的后半部分 id < 22,由于是范围查找,就会继续往后找第一个不满足条件的记录,也就是会找到 id = 25 这一行停下来,然后加 Next-key Lock (20, 25],重点来了,但由于 id = 25 不满足 id < 22,因此会退化成间隙锁,加锁范围变为 (20, 25)

所以,上述语句在主键 id 上的最终的加锁范围是 Record Lock id = 20 以及 Gap Lock (20, 25)

select * from user where id>20 for update;在事务 A 没有提交期间,其它事务不允许对id = 2530的记录更新、删除、锁定读,也不允许插入20< id < 25 || 25 < id < 30 || 30 < id的记录

范围查询 存在:

select * from user where id >20
for update;

加锁分析:范围查询,向右查询到的第一个是id=25。id=25的这个索引加上 (20,25]的next-key lock。意味着其他事务无法更新或者删除20,同事插入id=21,22,23,24的新纪录。

继续向右查询。

最终加锁是(20,25],(25,30],(30,正无穷)三个next-key lock。

案例 3:非唯一索引等值查询

当我们用非唯一索引进行等值查询的时候,根据查询的记录是否存在,加锁的规则会有所不同:

  1. 当查询的记录是存在的,除了会加 Next-key Lock 外,还会额外加间隙锁(规则是向下遍历到第一个不符合条件的值才能停止),也就是会加两把锁

    很好记忆,就是要查找记录的左区间加 Next-key Lock,右区间加 Gap lock

  2. 当查询的记录是不存在的,Next-key Lock 会退化成间隙锁(这个规则和唯一索引的等值查询是一样的)

查询的记录存在

先来看个查询的记录存在的案例:

select * from user where a = 16
for update;

结合加锁的两条核心,这条语句首先会对普通索引 a 加上 Next-key Lock,范围是 (8,16]

又因为是非唯一索引等值查询,且查询的记录 a= 16 是存在的,所以还会加上间隙锁,规则是向下遍历到第一个不符合条件的值才能停止,因此间隙锁的范围是 (16,32)

所以,上述语句在普通索引 a 上的最终加锁范围是 Next-key Lock (8,16] 以及 Gap Lock (16,32)

主键索引 record lock a=16这一行

查询的记录不存在

再来看查询的记录不存在的案例:

select * from user where a = 18
for update;

结合加锁的两条核心,这条语句首先会对普通索引 a 加上 Next-key Lock,范围是 (16,32]

但是由于查询的记录 a = 18 是不存在的,因此 Next-key Lock 会退化为间隙锁,即最终在普通索引 a 上的加锁范围是 (16,32)

案例 4:非唯一索引范围查询

范围查询和等值查询的区别在上面唯一索引章节已经介绍过了,就是范围查询需要一直向右遍历到第一个不满足条件的记录,和唯一索引范围查询不同的是,非唯一索引的范围查询并不会退化成 Record Lock 或者 Gap Lock。

select * from user where a >= 16 and a < 18
forupdate;

先来看语句查询条件的前半部分 a >= 16,因此,这条语句最开始要找的第一行是 a = 16,结合加锁的两个核心,需要加上 Next-key Lock (8,16]。虽然非唯一索引 a = 16 的这行记录是存在的,但此时并不会像唯一索引那样退化成记录锁。

再来看语句查询条件的后半部分 a < 18,由于是范围查找,就会继续往后找第一个不满足条件的记录,也就是会找到 id = 32 这一行停下来,然后加 Next-key Lock (16, 32]。虽然 id = 32 不满足 id < 18,但此时并不会向唯一索引那样退化成间隙锁。

所以,上述语句在普通索引 a 上的最终的加锁范围是 Next-key Lock (8, 16](16, 32],也就是 (8, 32]

MDL锁

元数据锁 (Metadata Lock,MDL) 是 server 层提供,不需要显示使用 MDL 锁,因为会根据操作自动为表添加 MDL:

  • 当事务执行 select、insert、delete、update 等 DML 语句时,会自动为表加 MDL 读锁,会阻塞 alter table、drop table 等操作
  • 当事务执行 alter table、drop table 等更改表结构的 DDL 语句时,会自动为表加 MDL 写锁,会阻塞 select、insert、delete、update 等操作

MDL 锁会持续整个事务执行期间,直到事务提交才会释放 MDL 锁。很有意思的一个特点:当表存在 MDL 读锁,然后其它事务对表加 MDL 写锁会阻塞,后续也无法对表加 MDL 读锁

因为对表加 MDL 锁的操作会形成一个队列,队列中加 MDL 写锁的优先级更高,所以后续也无法加 MDL 读锁。如果允许后续加 MDL 读锁,可能会导致加 MDL 写锁操作一直被阻塞

insert intention lock

  • 简单的insert会在insert的行对应的索引记录上加一个排它锁,这是一个record lock,并没有gap,所以并不会阻塞其他session在gap间隙里插入记录。不过在insert操作之前,还会加一种锁,官方文档称它为insertion intention gap lock,也就是意向的gap锁。这个意向gap锁的作用就是预示着当多事务并发插入相同的gap空隙时,只要插入的记录不是gap间隙中的相同位置,则无需等待其他session就可完成,这样就使得insert操作无须加真正的gap lock。
  • 假设有一个记录索引包含键值4和7,不同的事务分别插入5和6,每个事务都会产生一个加在4-7之间的插入意向锁,获取在插入行上的排它锁,但是不会被互相锁住,因为数据行并不冲突

参考来源

1、https://lfool.github.io/LFool-Notes/mysql/MySQL加锁实战分析.html

2、Mysql官网手册

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

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

相关文章

架构设计系列之基础设施能力建设

周末聊两句&#xff1a; 今天将的基础设施能力建设部分&#xff0c;一般的架构书籍中都不存在的部分&#xff0c;这是我在实践过程中的经验和能力总结部分&#xff0c;希望和大家有一个很好的交流自从在 WeChat 中开了订阅号的两周半的时间&#xff0c;非常感谢大家的支持&…

replace()是一个字符串替换的函数,用于将指定的字符串或模式在目标字符串中进行替换

replace()是一个字符串替换的函数&#xff0c;用于将指定的字符串或模式在目标字符串中进行替换。 函数原型如下&#xff1a; QString replace(const QString &before, const QString &after)参数说明&#xff1a; before&#xff1a;要替换的字符串或模式。after&a…

K - 近邻算法

1、算法介绍 KNN&#xff08;K Near Neighbor&#xff09;&#xff1a;k个最近的邻居&#xff0c;即每个样本都可以用它最接近的k个邻居来代表。KNN算法属于监督学习方式的分类算法&#xff0c;我的理解就是计算某给点到每个点的距离作为相似度的反馈。 简单来讲&#xff0c;KN…

nginx中的root and alias命令的区别

Ubuntu关于Nginx的命令&#xff1a; 1、安装Nginx&#xff1a; apt-get install nginx2、查看Nginx运行状态&#xff1a; systemctl status nginx3、启动Nginx&#xff1a; systemctl start nginx4、停止Nginx&#xff1a; systemctl stop nginx5、重启Nginx&#xff1a; …

代码随想录算法训练营第十八天 | 前中后序构造二叉树

目录 力扣题目 力扣题目记录 513.找树左下角的值 递归 迭代法 总结 112. 路径总和 106.从中序与后序遍历序列构造二叉树 总结 力扣题目 用时&#xff1a;2h 1、513.找树左下角的值 2、112. 路径总和 3、106.从中序与后序遍历序列构造二叉树 力扣题目记录 513.找树…

持续集成交付CICD:基于 GitLabCI 与 JenkinsCD 实现后端项目发布

目录 一、实验 1. GitLabCI环境设置 2.优化GitLabCI共享库代码 3.JenkinsCD 发布后端项目 4.再次优化GitLabCI共享库代码 5.JenkinsCD 再次发布后端项目 一、实验 1. GitLabCI环境设置 &#xff08;1&#xff09;GitLab给后端项目添加CI配置路径 &#xff08;2&#xf…

算法通关村第十二关—字符串冲刺题(黄金)

字符串冲刺题 一、最长公共前缀 LeetCode14 编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀&#xff0c;返回空字符串"" 示例1&#xff1a; 输入&#xff1a;strs["flower","fLow","flight"] 输出&#xff1a;&…

机器学习算法---时间序列

类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统计学检验箱…

【力扣100】21.合并两个有序链表

添加链接描述 # Definition for singly-linked list. # class ListNode: # def __init__(self, val0, nextNone): # self.val val # self.next next class Solution:def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -&…

SVPWM马鞍波形仿真(python)

SVPWM波的原理不再过多介绍。 最近在学习SVPWM&#xff0c;仿真了一下马鞍波。 python源码贡献出来。 import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as anim############################################# # 我们的目的是根据机械角度&…

12.16_黑马数据结构与算法笔记Java

目录 167 B树 remove 168 B树 remove 搭架子 169 B树 remove case1-4 170 B树 remove case5-6分析 171 B树 remove case5 旋转 172 B树 remove case5 合并 173 B树 remove case6 174 B树 remove 演示1 175 B树 remove 演示2 176 哈希表 概述 177 哈希表 hash码映射索…

新视野大学英语1 词组 12.17

embarrassment和awkwardness的区别以及各自的组词。 "Embarrassment" 和 "awkwardness" 都可以用来描述一种尴尬或不舒服的感觉&#xff0c;但它们有一些微妙的区别。 "Embarrassment" 指的是由于尴尬、困窘或难堪的情况而产生的感觉。 这种感觉…

XXE漏洞 [NCTF2019]Fake XML cookbook1

打开题目 查看源代码 发现我们post传入的数据都被放到了doLogin.php下面 访问一下看看 提示加载外部xml实体 bp抓包一下看看 得到flag 或者这样 但是很明显这样是不行的&#xff0c;因为资源是在admin上&#xff0c;也就是用户名那里 PHP引用外部实体&#xff0c;常见的利用…

串口通信(5)-C#串口通信数据接收不完整解决方案

本文讲解C#串口通信数据接收不完整解决方案。 目录 一、概述 二、Modbus RTU介绍 三、解决思路 四、实例 一、概述 串口处理接收数据是串口程序编写的关键

(2)Linux 操作系统||基本创建与操作

本章将浅谈一下 "操作系统是什么" 的问题&#xff0c;随后通过讲解一些 Linux 下的基本指令&#xff0c;显示目录内容、跳转操作和文件的创建与删除。在讲解的同时我会穿插一些知识点&#xff0c;比如 Linux 隐藏文件、路径等基础知识。 了解操作系统 什么是操作系统…

【Java 并发】三大特性

在 Java 的高并发中&#xff0c;对于线程并发问题的分析通常可以通过 2 个主核心进行分析 JMM 抽象内存模型和 Happens-Before 规则三大特性: 原子性, 有序性和可见性 JMM 抽象内存模型和 Happens-Before 规则, 前面我们讨论过了。这里讨论一下三大特性。 1 原子性 定义: 一个…

第十一章 算法复杂度

11.1 大O表示法 它用于描述算法的性能和复杂程度。分析算法时&#xff0c;时常遇到以下几类函数&#xff1a; 11.1.1 理解大O表示法 如何衡量算法的效率&#xff1f;通常是用资源&#xff0c;例如CPU&#xff08;时间&#xff09;占用、内存占用、硬盘占用和网络 占用。当讨论…

时序预测 | Python实现GRU电力需求预测

时序预测 | Python实现GRU电力需求预测 目录 时序预测 | Python实现GRU电力需求预测预测效果基本描述程序设计参考资料预测效果 基本描述 该数据集因其每小时的用电量数据以及 TSO 对消耗和定价的相应预测而值得注意,从而可以将预期预测与当前最先进的行业预测进行比较。使用该…

docker:安装docker项目的问题记录

windows下拉取的项目&#xff0c;会导致sh文件带的换行符不一致导致报错。原来的docker文件源过期&#xff0c;需要网上找新的源来替换。缺少未知的配置文件&#xff0c;比如.env或者其他文件。需要其他同事告知&#xff0c;readme里面没有文档说明是坑。加减agt-get install时…

同义词替换在论文降重中的实际效果评估 快码论文

大家好&#xff0c;今天来聊聊同义词替换在论文降重中的实际效果评估&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff0c;可以借助此类工具&#xff1a; 标题&#xff1a;同义词替换在论文降重中的实际效果评…