MySQL中锁的几种类型

MySQL根据加锁的范围,可以分为全局锁、表级锁、行级锁三类。

2.5.1. 锁定读

2.5.1.1. 共享锁和独占锁

事务的 读-读 情况并不会引起什么问题,对于 写-写、读-写 或 写-读 这些情况可能会引起一些问题,需要使用MVCC或者加锁的方式来解决。在使用加锁的方式解决问题时,由于既要允许 读-读 情况不受影响,又要使 写-写、读-写 或 写-读 情况中的操作相互阻塞,所以MySQL给锁分了个类:

  • 共享锁,Shared Locks,简称 S 锁。在事务要读取一条记录时,需要先获取该记录的 S 锁。
  • 独占锁,也常称排他锁,英文名:Exclusive Locks,简称 X 锁。在事务要改动一条记录时,需要先获取该记录的X锁。

事务 T1 首先获取了一条记录的 X 锁之后,那么不管事务 T2 接着想获取该记录的 S 锁还是 X 锁都会被阻塞,直到事务 T1 提交。

2.5.1.2. 锁定读语句

有时候想在读取记录时就获取记录的 X 锁,来禁止别的事务读写该记录,为此MySQL存在两种比较特殊的SELECT语句格式:

  1. 对读取的记录加 S 锁:
SELECT ... LOCK IN SHARE MODE;

在普通的SELECT语句后边加LOCK IN SHARE MODE,当前事务执行了该语句,会为读取到的记录加S锁,允许别的事务继续获取这些记录的S锁,但是不能获取这些记录的X锁。别的事务想获取这些记录的 X 锁,就会阻塞,直到当前事务提交之后将这些记录上的S锁释放掉。

  1. 对读取的记录加X锁:
SELECT ... FOR UPDATE;

在普通的SELECT语句后边加FOR UPDATE,当前事务执行了该语句,会为读取到的记录加X锁,这样既不允许别的事务获取这些记录的S锁,也不允许获取这些记录的X锁。如果别的事务想要获取这些记录的S锁或者X锁,就会阻塞,直到当前事务提交之后将这些记录上的X锁释放掉。

2.5.2. 全局锁

全局锁使用方法:

-- 加全局锁
flush tables with read lock
-- 释放全局锁
unlock tables

加了全局锁后,整个数据库处于只读状态,其他线程执行以下状态都会被阻塞:

  • 对数据库的增删改操作,如 insert、delete、update等语句;
  • 对表结构的更改操作,比如 alter table、drop table 等语句。

会话断开后,全局锁自动释放

全局锁的应用场景:

主要用于做全库逻辑备份,备份数据库期间不会因为数据或表结构的更新,造成数据不一致。

例如:

全库逻辑备份期间,用户购买了一件商品,商品业务逻辑涉及多张数据库表的更新;

  1. 先备份了用户表的数据
  2. 用户购买了商品
  3. 备份商品表的数据

备份用户表和商品表之间用户购买了商品,备份的结果就是用户余额没有减少,库存减少了。

全局锁带来的缺点:

整个数据库都是只读状态,庞大的数据量进行备份会花费很长时间,而且不能更新数据,会造成业务停滞。

避免全局锁会影响业务:

数据库引擎的事务支持可重复读隔离级别,备份前先开启事务,会先创建Read View,整个事务期间都在用这个Read View,由于MVCC的支持,备份期间业务依然可以对数据进行更新,这就是隔离性。

备份数据库的工具是 mysqldump,使用 mysqldump 时加上 –single-transaction 参数,就会在备份数据库之前先开启事务。这种方法只适用于支持「可重复读隔离级别的事务」的存储引擎。

InnoDB 存储引擎可以采用这种方式来备份数据库,对于 MyISAM 这种不支持事务的引擎,在备份数据库时就要使用全局锁的方法。

2.5.3. 表级锁

MySQL 里面表级别的锁有这几种:

  • 表锁;
  • 元数据锁(MDL);
  • 意向锁;
  • AUTO-INC 锁;
2.5.3.1. 表锁

加表锁可使用以下命令:

//表级别的共享锁,也就是读锁;
lock tables t_student read;//表级别的独占锁,也就是写锁;
lock tables t_stuent write;
-- 释放当前会话所有表锁 会话退出也会释放所有表锁
unlock tables

本线程对学生表加了「共享表锁」,任何写操作(包括当前线程和其他线程的写操作)都会被阻塞,直到锁被释放。

本线程对学生表加了「独占表锁」,该锁持有期间,当前线程可以对表进行任何操作(读写),其他事务无法对该表进行任何类型的锁定(包括共享锁和其他独占锁),保证了当前事务对该表的独占访问权限,通常在事务结束时(提交或回滚)释放。

尽量避免在使用 InnoDB 引擎的表使用表锁,因为表锁的颗粒度太大,会影响并发性能。

2.5.3.2. 元数据锁(MDL)

对数据库表进行操作时,会自动给这个表加上 MDL:

  • 对一张表进行 CRUD 操作时,加的是 MDL 读锁
  • 对一张表做结构变更操作的时候,加的是 MDL 写锁

防止对用户表进行CRUD时,其他线程对这个表结构做了变更。

当有线程在执行 select 语句( 加 MDL 锁)的期间,其他线程要更改该表的结构( 申请 MDL 写锁),将会被阻塞,直到执行完 select 语句( 释放 MDL 读锁)。

当有线程对表结构进行变更( 加 MDL 锁)的期间,其他线程执行了 CRUD 操作( 申请 MDL 读锁),就会被阻塞,直到表结构变更完成( 释放 MDL 写锁)。

MDL不需要显示调用,它是在什么时候释放的?

事务执行期间MDL会一直持有,事务提交后会释放。

一个长事务(开启了未提交),对表结构进行变更操作可能会出现的问题:

  1. 线程A开启事务,执行select语句,表上会加MDL读锁;
  2. 线程B执行查询语句,此时不会阻塞,读读 不冲突;
  3. 线程C修改表字段,MDL读锁还在占用,线程C无法申请到MDL写锁,会被阻塞
  4. 后续对该表的查询语句都会被阻塞,如果有大量的查询语句,线程很快就会爆满

因为申请MDL锁的操作会形成队列,写锁获取优先级高于读锁,出现写锁后续的CRUD都会被阻塞。

2.5.3.3. 意向锁
  • 意向共享锁,英文名:Intention Shared Lock,简称IS锁。当事务准备在某条记录上加S锁时,需要先在表级别加一个IS锁。
  • 意向独占锁,英文名:Intention Exclusive Lock,简称IX锁。当事务准备在某条记录上加X锁时,需要先在表级别加一个IX锁。

IS、IX锁是表级锁,它们的提出仅仅为了在之后加表级别的S锁和X锁时可以快速判断表中的记录是否被上锁,以避免用遍历的方式来查看表中有没有上锁的记录,也就是说其实IIS锁和IX锁是兼容的,IX锁和IX锁是兼容的

兼容性

X

IX

S

IS

X

不兼容

不兼容

不兼容

不兼容

IX

不兼容

兼容

不兼容

兼容

S

不兼容

不兼容

兼容

兼容

IS

不兼容

兼容

兼容

兼容

执行插入、更新、删除操作,需要先对表加上「意向独占锁」,然后对该记录加独占锁。

普通的 select 是不会加行级锁的,普通的 select 语句是利用 MVCC 实现一致性读,是无锁的。

select 也是可以对记录加共享锁和独占锁的,具体方式如下:

//先在表上加上意向共享锁,然后对读取的记录加共享锁
select ... lock in share mode;//先在表上加上意向独占锁,然后对读取的记录加独占锁
select ... for update;

2.5.3.4. AUTO-INC锁

MySQL 5.1.22 版本开始,InnoDB 存储引擎提供了一种轻量级的锁来实现自增。

插入数据的时候,为被 AUTO_INCREMENT 修饰的字段加上轻量级锁,然后给该字段赋一个自增的值,就把这个轻量级锁释放了,而不需要等待整个插入语句执行完后才释放锁

innodb_autoinc_lock_mode 系统变量,是用来控制选择用 AUTO-INC 锁,还是轻量级的锁。

  • 当 innodb_autoinc_lock_mode = 0,就采用 AUTO-INC 锁,语句执行结束后才释放锁;
  • 当 innodb_autoinc_lock_mode = 2,就采用轻量级锁,申请自增主键后就释放锁,并不需要等语句执行后才释放。
  • 当 innodb_autoinc_lock_mode = 1:
    • 普通 insert 语句,自增锁在申请之后就马上释放;
    • 类似 insert … select 这样的批量插入数据的语句,自增锁还是要等语句结束后才被释放;

innodb_autoinc_lock_mode = 2 是性能最高的方式,但是当搭配 binlog 的日志格式是 statement 一起使用的时候,在「主从复制的场景」中会发生数据不一致的问题

当 innodb_autoinc_lock_mode = 2 时,并且 binlog_format = row,既能提升并发性,又不会出现数据一致性问题

具体案例查看参考文档。

2.5.4. 行级锁(记录锁)

InnoDB 引擎是支持行级锁的,而 MyISAM 引擎并不支持行级锁。

普通的 select 语句属于快照读不会对记录加锁。要在查询时对记录加行锁,可以使用下面这两个方式,这种查询会加锁的语句称为锁定读

//对读取的记录加共享锁
select ... lock in share mode;//对读取的记录加独占锁
select ... for update;

上面这两条语句必须在一个事务中,因为当事务提交了,锁就会被释放,所以在使用这两条语句的时候,要加上 begin、start transaction 或者 set autocommit = 0。

共享锁(S锁)满足读读共享,读写互斥。独占锁(X锁)满足写写互斥、读写互斥。

行级锁的类型主要有三类:

  • Record Lock,记录锁,也就是仅仅把一条记录锁上;
  • Gap Lock,间隙锁,锁定一个范围,但是不包含记录本身;
  • Next-Key Lock:Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身。
2.5.4.1. Record Lock

Record Lock 称为记录锁,锁住的是一条记录。而且记录锁是有 S 锁(共享锁)和 X 锁(独占锁)之分的:

  • 一个事务对一条记录加了 S 锁后,其他事务也可以继续对该记录加 S锁(S 锁与 S 锁兼容),但是不可以对该记录加 X锁(S 锁与 X 锁不兼容);
  • 一个事务对一条记录加了 X锁后,其他事务既不可以对该记录加 S 锁(S 锁与 X 锁不兼容),也不可以对该记录加 X 锁(X 锁与 X 锁不兼容)。
2.5.4.2. Gap Lock

Gap Lock 称为间隙锁,只存在于可重复读隔离级别,目的是为了解决可重复读隔离级别下幻读的现象。

表中有一个范围 id 为(3,5)间隙锁,那么其他事务就无法插入 id = 4 这条记录,有效的防止幻读现象的发生。

间隙锁虽然存在 X 型间隙锁和 S 型间隙锁,但是并没有什么区别,间隙锁之间是兼容的,即两个事务可以同时持有包含共同间隙范围的间隙锁,并不存在互斥关系,因为间隙锁的目的是防止插入幻读记录而提出的

给一条记录加了gap锁只是不允许其他事务往这条记录前面的间隙插入新记录,那对于最后一条记录之后的间隙,该咋办呢?两条伪记录:

  • Infimum记录,表示该页面中最小的记录。
  • Supremum记录,表示该页面中最大的记录。

为了实现阻止其他事务在该记录后插入新记录,可以给索引中的最后一条记录加上一个gap锁

详细案例查看参考文档。

2.5.4.3. Next-Key Lock

Next-Key Lock 称为临键锁,是 Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身。

假设,表中有一个范围 id 为(3,5] 的 next-key lock,那么其他事务即不能插入 id = 4 记录,也不能修改 id = 5 这条记录。

next-key lock 是包含间隙锁+记录锁的,如果一个事务获取了 X 型的 next-key lock,那么另外一个事务在获取相同范围的 X 型的 next-key lock 时,是会被阻塞的

相同范围的间隙锁是多个事务相互兼容的,但对于记录锁,要考虑 X 型与 S 型关系,X 型的记录锁与 X 型的记录锁是冲突的。

2.5.4.4. MySQL行级锁的加锁规则

唯一索引等值查询:

  • 当查询的记录「存在」,在索引树上定位到这一条记录后,该记录的索引中的 next-key lock 会退化成「记录锁」
  • 当查询的记录「不存在」,在索引树找到第一条大于该查询记录的记录后,该记录的索引中的 next-key lock 会退化成「间隙锁」

非唯一索引等值查询:

  • 当查询的记录「存在」时,可能存在索引值相同的记录,所以非唯一索引等值查询的过程是一个扫描的过程,扫描到第一个不符合条件的二级索引记录就停止扫描,然后在扫描的过程中,对扫描到的二级索引记录加的是 next-key 锁,而对于第一个不符合条件的二级索引记录,该二级索引的 next-key 锁会退化成间隙锁。同时,在符合查询条件的记录的主键索引上加记录锁
  • 当查询的记录「不存在」时,扫描到第一条不符合条件的二级索引记录,该二级索引的 next-key 锁会退化成间隙锁。因为不存在满足查询条件的记录,所以不会对主键索引加锁

非唯一索引和主键索引的范围查询加锁规则不同之处在于:

  • 唯一索引在满足一些条件的时候,索引的 next-key lock 退化为间隙锁或者记录锁。
  • 非唯一索引范围查询,索引的 next-key lock 不会退化为间隙锁和记录锁。

在线上在执行 update、delete、select ... for update 等具有加锁性质的语句,一定要检查语句是否走了索引,如果是全表扫描的话,会对每一个索引加 next-key 锁,相当于把整个表锁住了,这是挺严重的问题。

唯一索引(主键索引)加锁的流程图如下。(如果是二级索引的唯一索引,除了流程图中对二级索引的加锁规则之外,还会对查询到的记录的主键索引项加「记录锁」,流程图没有提示这一个点,所以在这里用文字补充说明下

非唯一索引加锁的流程图:

2.5.5. 插入意向锁

一个事务在插入一条记录的时候,需要判断插入位置是否已被其他事务加了间隙锁(next-key lock 也包含间隙锁)。

如果有的话,插入操作就会发生阻塞,直到拥有间隙锁的那个事务提交为止(释放间隙锁的时刻),在此期间会生成一个插入意向锁,表明有事务想在某个区间插入新记录,但是现在处于等待状态。

插入意向锁名字虽然有意向锁,但是它并不是意向锁,它是一种特殊的间隙锁,属于行级别锁

如果说间隙锁锁住的是一个区间,那么「插入意向锁」锁住的就是一个点。

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

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

相关文章

15:00面试,15:08就出来了,问的问题有点变态。。。

从小厂出来,没想到在另一家公司又寄了。 到这家公司开始上班,加班是每天必不可少的,看在钱给的比较多的份上,就不太计较了。没想到8月一纸通知,所有人不准加班,加班费不仅没有了,薪资还要降40%…

MySQL数据库下的Explain命令深度解析

Explain是一个非常有的命令,可以用来获取关于查询执行计划的信息,以及如何解释输出。Explain命令是查看查询优化器如何决定执行查询的主要方法。这个功能有一定的局限性,并不总是会说出真相,但是它的输出是可以获取的最好信息&…

Kubernetes集群上的Etcd备份和恢复

在本教程中,您将学习如何在Kubernetes集群上使用etcd快照进行etcd备份和恢复。 在Kubernetes架构中,etcd是集群的重要组成部分。所有集群对象及其状态都存储在etcd中。为了更好地理解Kubernetes,有几点关于etcd的信息是您需要了解的。 它是…

【探索数据结构】线性表之双链表

🎉🎉🎉欢迎莅临我的博客空间,我是池央,一个对C和数据结构怀有无限热忱的探索者。🙌 🌸🌸🌸这里是我分享C/C编程、数据结构应用的乐园✨ 🎈🎈&…

【超全干货】一文讲清什么是全民分销?怎么做好全民分销?

一、什么是全民分销? 全民分销,作为新时代营销模式的代表之一,是基于互联网尤其是社交媒体平台兴起的一种分销策略。它打破了传统零售与电子商务的界限,允许任何个人,无论是否为专业销售人员,都能成为品牌…

【WEB前端2024】开源智体世界:乔布斯3D纪念馆-第27课-门的打开

【WEB前端2024】开源智体世界:乔布斯3D纪念馆-第27课-门的打开 使用dtns.network德塔世界(开源的智体世界引擎),策划和设计《乔布斯超大型的开源3D纪念馆》的系列教程。dtns.network是一款主要由JavaScript编写的智体世界引擎&am…

FreeRTOS_互斥量_学习笔记

互斥量 数值只有0或1 谁获得互斥量,就必须由谁释放同一个互斥量。 但其实在freeRTOS中,任务A获取的互斥锁,任务B也能释放。因此谁上锁谁开锁只是约定,在程序实现上不是强制的。 “可重入的函数"是指:多个任务同时…

Qt输入输出类使用总结

Qt输入输出类简介 QTextStream 类(文本流)和 QDataStream 类(数据流)Qt 输入输出的两个核心类,其作用分别如下: QTextStream 类:用于对数据进行文本格式的读/写操作,可在 QString、QIODevice或 QByteArray 上运行,比如把数据输出到 QString、QIODevice 或 QByteArray 对象…

Linux-文件或目录权限

在使用 ll 时,可以查看文件夹内容的详细信息,信息的第1位表示类型,具体信息如下: 类型说明-普通文件d文件夹b块设备文件c字符设备文件p管道文件s套接口文件 第2-10位表示权限, 举例:rwxr-xr-x 类型说明r…

业务架构核心要素之间的关系

背景 前面已经对业务架构的概念和发展简史有了初步的了解,现在主流的业务架构就是价值流能力,在这套架构体系中,有四个核心元素,分别是价值流、业务能力、信息、组织。 这四个核心元素,特别是对于价值流以及业务能力…

查看主机的php参数short_open_tag 是否为 on

我想要查看主机的php参数short_open_tag 是否为 on,由于我使用的是Hostease的Linux虚拟主机产品,在cPanel面板中并没有找到这个参数选项,因此无法查看。这边联系了Hostease技术支持了解,可以通过以下方式进行查看。 1.先登陆cPane…

Shell编程之条件判断语句

目录 一、条件判断 1、test命令 2、文件测试 3、整数值比较 4、字符串判断 5、逻辑测试 二、if语句 1、if单分支语句 2、双分支语句 3、多分之语句 4、case 分支语句 一、条件判断 Shell环境根据命令执行后的返回状态值(echo $?)来判断是否执行成…

鸿蒙HarmonyOS开发中的易混点归纳-持续补充中

相关文章目录 鸿蒙HarmonyOS开发术语全解:小白也能看懂! 文章目录 相关文章目录前言一、build()函数和Builder装饰器?二、自定义组件和系统组件(内置组件)三、组件和页面四、自定义弹窗和其他弹窗总结 前言 一、build…

SpringBoot(五)之新增注解

SpringBoot(五)之新增注解 文章目录 SpringBoot(五)之新增注解核心注解EnableAutoConfiguration**SpringBootApplication** 条件注解Spring原生Conditional注解手写Conditional注解**ConditionalOnProperty****ConditionalOnMissi…

Logrus IT的供应商经理如何找到优秀地翻译人员

在现代世界,翻译和本地化可以使企业打入新市场并与来自不同国家的客户互动,从而对企业的成功起到决定性作用。翻译过程中的一个关键部分是寻找和招聘翻译。在Logrus IT,这是Anna Gulenkova的工作,我们决定和她谈谈她是如何做到的。…

BUUCTF---misc---我吃三明治

1、下载附件是一张图片 2、在winhex分析,看到一串整齐的编码有点可疑,保存下来,拿去解码,发现解不了,看来思路不对 3、再仔细往下看的时候也发现了一处这样的编码,但是这次编码后面多了一段base编码 4、拿去…

最长前缀数组

欢迎关注更多精彩 关注我,学习常用算法与数据结构,一题多解,降维打击。 基本问题 给定一个字符串s, 返回一个数组Z, Z[i]代表子串s[i…n] 与s最长公共前缀的长度。 朴素做法 可以枚举所有s[i…n]子串,然后与s一一比较&#x…

ERROR TypeError: Cannot read property ‘tapPromise‘ of undefined

问题概要 vue项目运行npm run build打包时,报了ERROR TypeError: Cannot read property ‘tapPromise’ of undefined TypeError: Cannot read property ‘tapPromise’ of undefined的错误。 解决方式 根据github中compression-webpack-plugin源码issues里的提示 …

C#--SVG矢量图画法示例

1.代码示例 <Viewbox Grid.Column"1" Grid.ColumnSpan"1" Grid.RowSpan"1" ><Path Name"ValveShape" Stroke"Black" Data"M 50,0 L 150,200 L 50,200 L 150,0 Z" Width"200" Height"…

经常出差用哪些办公软件记录工作?可多设备同步使用的便签笔记软件

对于许多职场人士来说&#xff0c;出差已成为工作常态。在旅途中&#xff0c;如何高效处理工作&#xff0c;确保信息不遗漏&#xff0c;成为了一个不小的挑战。那么&#xff0c;对于经常需要移动办公的我们&#xff0c;哪款办公软件才是最佳选择呢&#xff1f;可多设备同步使用…