MySQL 中的锁(一)

MySQL 中的锁

按照 MySQL 官方的说法,InnoDB 中锁可以分为:
在这里插入图片描述
可见,InnoDB 中锁非常多,总的来说,可以如下分类:
在这里插入图片描述
这些锁都是做什么的?具体含义是什么?我们现在来一一学习。

8.1. 解决并发事务问题

我们已经知道事务并发执行时可能带来的各种问题,最大的一个难点是:一
方面要最大程度地利用数据库的并发访问,另外一方面还要确保每个用户能以一
致的方式读取和修改数据,尤其是一个事务进行读取操作,另一个同时进行改动
操作的情况下。

8.1.1. 复习并发事务问题

一个事务进行读取操作,另一个进行改动操作,我们前边说过,这种情况下
可能发生脏读、不可重复读、幻读的问题。
SQL 标准规定不同隔离级别下可能发生的问题不一样:
在 READ UNCOMMITTED 隔离级别下,脏读、不可重复读、幻读都可能发生。
在 READ COMMITTED 隔离级别下,不可重复读、幻读可能发生,脏读不可以发生。
在 REPEATABLE READ 隔离级别下,幻读可能发生,脏读和不可重复读不可以发生。
在 SERIALIZABLE 隔离级别下,上述问题都不可以发生。
不过各个数据库厂商对 SQL 标准的支持都可能不一样,与 SQL 标准不同的一
点就是,MySQL 在 REPEATABLE READ 隔离级别实际上就基本解决了幻读问题。
怎么解决脏读、不可重复读、幻读这些问题呢?其实有两种可选的解决方案:

8.1.2. 方案一:读操作 MVCC,写操作进行加锁

所谓的 MVCC 我们在前一章有过详细的描述,就是通过生成一个 ReadView,
然后通过 ReadView 找到符合条件的记录版本(历史版本是由 undo 日志构建的),
其实就像是在生成 ReadView 的那个时刻做了一个快照,查询语句只能读到在生
成 ReadView 之前已提交事务所做的更改,在生成 ReadView 之前未提交的事务或
者之后才开启的事务所做的更改是看不到的。而写操作肯定针对的是最新版本的
记录,读记录的历史版本和改动记录的最新版本本身并不冲突,也就是采用
MVCC 时,读-写操作并不冲突。

我们说过普通的 SELECT 语句在 READ COMMITTED 和 REPEATABLE READ 隔离
级别下会使用到 MVCC 读取记录。在 READ COMMITTED 隔离级别下,一个事务
在执行过程中每次执行 SELECT 操作时都会生成一个 ReadView,ReadView 的存在
本身就保证了事务不可以读取到未提交的事务所做的更改,也就是避免了脏读现
象;REPEATABLE READ 隔离级别下,一个事务在执行过程中只有第一次执行
SELECT 操作才会生成一个 ReadView,之后的 SELECT 操作都复用这个 ReadView,
这样也就避免了不可重复读和很大程度上避免了幻读的问题。

8.1.3. 一致性读(Consistent Reads)/快照读

事务利用 MVCC 进行的读取操作称之为一致性读,或者一致性无锁读,也称
之为快照读,但是往往读取的是历史版本数据。所有普通的 SELECT 语句(plain
SELECT)在 READ COMMITTED、REPEATABLE READ 隔离级别下都算是一致性读。
上面的这句话中,普通的 SELECT 语句是指不加锁的 select 语句在非串行化事务隔离级别下。

一致性读并不会对表中的任何记录做加锁操作,其他事务可以自由的对表中的记录做改动。

很明显,采用 MVCC 方式的话,读-写操作彼此并不冲突,性能更高,采用
加锁方式的话,读-写操作彼此需要排队执行,影响性能。一般情况下我们当然
愿意采用 MVCC 来解决读-写操作并发执行的问题,但是业务在某些情况下,要
求必须采用加锁的方式执行。

8.1.4. 方案二:读、写操作都采用加锁的方式

如果我们的一些业务场景不允许读取记录的旧版本,而是每次都必须去读取
记录的最新版本,比方在银行存款的事务中,你需要先把账户的余额读出来,然
后将其加上本次存款的数额,最后再写到数据库中。在将账户余额读取出来后,
就不想让别的事务再访问该余额,直到本次存款事务执行完成,其他事务才可以
访问账户的余额。这样在读取记录的时候也就需要对其进行加锁操作,这样也就
意味着读操作和写操作也像写-写操作那样排队执行。

我们说脏读的产生是因为当前事务读取了另一个未提交事务写的一条记录,
如果另一个事务在写记录的时候就给这条记录加锁,那么当前事务就无法继续读取该记录了,所以也就不会有脏读问题的产生了。

不可重复读的产生是因为当前事务先读取一条记录,另外一个事务对该记录做了改动之后并提交之后,当前事务再次读取时会获得不同的值,如果在当前事务读取记录时就给该记录加锁,那么另一个事务就无法修改该记录,自然也不会发生不可重复读了。

我们说幻读问题的产生是因为当前事务读取了一个范围的记录,然后另外的事务向该范围内插入了新记录,当前事务再次读取该范围的记录时发现了新插入的新记录,我们把新插入的那些记录称之为幻影记录。采用加锁的方式解决幻读问题就有不太容易了,因为当前事务在第一次读取记录时那些幻影记录并不存在,所以读取的时候加锁就有点麻烦 —— 因为并不知道给谁加锁。InnoDB 中是如何解决的,我们后面会讲到。

8.2. 锁定读(Locking Reads)/LBCC

也称当前读, 读取的是最新版本, 并且对读取的记录加锁, 阻塞其他事务同时改动相同记录,避免出现安全问题。
哪些是当前读呢?select lock in share mode (共享锁)、select for update (排他
锁)、update (排他锁)、insert (排他锁)、delete (排他锁)、串行化事务隔离级别都是当前读。

当前读这种实现方式,也可以称之为 LBCC(基于锁的并发控制,Lock-Based
Concurrency Control),怎么做到?

8.2.1.1. 共享锁和独占锁

在使用加锁的方式解决问题时,由于既要允许读-读情况不受影响,又要使
写-写、读-写或写-读情况中的操作相互阻塞,MySQL 中的锁有好几类:

  • 共享锁,英文名:Shared Locks,简称 S 锁。在事务要读取一条记录时,需要先获取该记录的 S 锁。

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

  • 假如事务 E1 首先获取了一条记录的 S 锁之后,事务 E2 接着也要访问这条记录:

  • 如果事务 E2 想要再获取一个记录的 S 锁,那么事务 E2 也会获得该锁,也就意味着事务 E1 和 E2 在该记录上同时持有 S 锁。

  • 如果事务 E2 想要再获取一个记录的 X 锁,那么此操作会被阻塞,直到事务E1 提交之后将 S 锁释放掉。

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

  • 所以我们说 S 锁和 S 锁是兼容的,S 锁和 X 锁是不兼容的,X 锁和 X 锁也是不兼容的,画个表表示一下就是这样:

X 不兼容 X 不兼容 S
S 不兼容 X 兼容 S

8.2.1.2. 锁定读的 SELECT 语句

MySQ 有两种比较特殊的 SELECT 语句格式:
对读取的记录加 S 锁:
SELECT … LOCK IN SHARE MODE;

也就是在普通的 SELECT 语句后边加 LOCK IN SHARE MODE,如果当前事务执
行了该语句,那么它会为读取到的记录加 S 锁,这样允许别的事务继续获取这些
记录的 S 锁(比方说别的事务也使用 SELECT … LOCK IN SHARE MODE 语句来读取
这些记录),但是不能获取这些记录的 X 锁(比方说使用 SELECT … FOR UPDATE
语句来读取这些记录,或者直接修改这些记录)。

如果别的事务想要获取这些记录的 X 锁,那么它们会阻塞,直到当前事务提交之后将这些记录上的 S 锁释放掉。
对读取的记录加 X 锁:
SELECT … FOR UPDATE;
也就是在普通的 SELECT 语句后边加 FOR UPDATE,如果当前事务执行了该语
句,那么它会为读取到的记录加 X 锁,这样既不允许别的事务获取这些记录的 S
锁(比方说别的事务使用 SELECT … LOCK IN SHARE MODE 语句来读取这些记录),
也不允许获取这些记录的 X 锁(比如说使用 SELECT … FOR UPDATE 语句来读取这
些记录,或者直接修改这些记录)。
如果别的事务想要获取这些记录的 S 锁或者 X 锁,那么它们会阻塞,直到当前事务提交之后将这些记录上的 X 锁释放掉。

8.2.1.3. 写操作的锁

平常所用到的写操作无非是 DELETE、UPDATE、INSERT 这三种:

DELETE:
对一条记录做 DELETE 操作的过程其实是先在 B+树中定位到这条记录的位置,
然后获取一下这条记录的 X 锁,然后再执行 delete mark 操作。我们也可以把这
个定位待删除记录在 B+树中位置的过程看成是一个获取 X 锁的锁定读。
INSERT:
一般情况下,新插入一条记录的操作并不加锁,InnoDB 通过一种称之为隐
式锁来保护这条新插入的记录在本事务提交前不被别的事务访问。当然,在一些
特殊情况下 INSERT 操作也是会获取锁的,具体情况我们后边再说。

UPDATE:
在对一条记录做 UPDATE 操作时分为三种情况:
如果未修改该记录的键值并且被更新的列占用的存储空间在修改前后未发
生变化,则先在 B+树中定位到这条记录的位置,然后再获取一下记录的 X 锁,
最后在原记录的位置进行修改操作。其实我们也可以把这个定位待修改记录在
B+树中位置的过程看成是一个获取 X 锁的锁定读。

如果未修改该记录的键值并且至少有一个被更新的列占用的存储空间在修改前后发生变化,则先在 B+树中定位到这条记录的位置,然后获取一下记录的 X锁,将该记录彻底删除掉(就是把记录彻底移入垃圾链表),最后再插入一条新记录。这个定位待修改记录在 B+树中位置的过程看成是一个获取 X 锁的锁定读,新插入的记录由 INSERT 操作提供的隐式锁进行保护。

如果修改了该记录的键值,则相当于在原记录上做 DELETE 操作之后再来一次 INSERT 操作,加锁操作就需要按照 DELETE 和 INSERT 的规则进行了。

8.3. 锁的粒度

我们前边提到的锁都是针对记录的,也可以被称之为行级锁或者行锁,对一条记录加锁影响的也只是这条记录而已,我们就说这个锁的粒度比较细;其实一个事务也可以在表级别进行加锁,自然就被称之为表级锁或者表锁,对一个表加锁影响整个表中的记录,我们就说这个锁的粒度比较粗。给表加的锁也可以分为共享锁(S 锁)和独占锁(X 锁)

8.3.1.1. 表锁与行锁的比较

锁定粒度:表锁 > 行锁
加锁效率:表锁 > 行锁
冲突概率:表锁 > 行锁
并发性能:表锁 < 行锁

8.3.1.2. 给表加 S 锁

如果一个事务给表加了 S 锁,那么:
别的事务可以继续获得该表的 S 锁
别的事务可以继续获得该表中的某些记录的 S 锁
别的事务不可以继续获得该表的 X 锁
别的事务不可以继续获得该表中的某些记录的 X 锁

8.3.1.3. 给表加 X 锁

如果一个事务给表加了 X 锁(意味着该事务要独占这个表),那么:
别的事务不可以继续获得该表的 S 锁
别的事务不可以继续获得该表中的某些记录的 S 锁
别的事务不可以继续获得该表的 X 锁
别的事务不可以继续获得该表中的某些记录的 X 锁。
为了更好的理解这个表级别的 S 锁和 X 锁和后面的意向锁,我们举一个现实
生活中的例子。我们用曾经很火爆的互联网风口项目共享 Office 来说明加锁:

共享 Office 有栋大楼,楼自然有很多层。办公室都是共享的,客户可以随便选办公室办公。每层楼可以容纳客户同时办公,每当一个客户进去办公,就相当于在每层的入口处挂了一把 S 锁,如果很多客户进去办公,相当于每层的入口处挂了很多把 S 锁(类似行级别的 S 锁)。

有的时候楼层会进行检修,比方说换地板,换天花板,检查水电啥的,这些维修项目并不能同时开展。如果楼层针对某个项目进行检修,就不允许客户来办公,也不允许其他维修项目进行,此时相当于楼层门口会挂一把 X 锁(类似行级别的 X 锁)。

上边提到的这两种锁都是针对楼层而言的,不过有时候我们会有一些特殊的需求:

A、有投资人要来考察 Office 的环境。
投资人和公司并不想影响客户进去办公,但是此时不能有楼层进行检修,所以可以在大楼门口放置一把 S 锁(类似表级别的 S 锁)。此时:来办公的客户们看到大楼门口有 S 锁,可以继续进入大楼办公。
修理工看到大楼门口有 S 锁,则先在大楼门口等着,啥时候投资人走了,把大楼的 S 锁撤掉再进入大楼维修。

B、公司要和房东谈条件。
此时不允许大楼中有正在办公的楼层,也不允许对楼层进行维修。所以可以在大楼门口放置一把 X 锁(类似表级别的 X 锁)。此时:来办公的客户们看到大楼门口有 X 锁,则需要在大楼门口等着,啥时候条件谈好,把大楼的 X 锁撤掉再进入大楼办公。
修理工看到大楼门口有 X 锁,则先在大楼门口等着,啥时候考试结束,把大楼的 X 锁撤掉再进入大楼维修。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于YOLOv8深度学习的生活垃圾分类目标检测系统【python源码+Pyqt5界面+数据集+训练代码】目标检测

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

数据结构-选择排序(简单选择、堆)

简单选择排序 基本思想 非常基础的算法&#xff0c;假设有N个数据&#xff0c;比较N-1轮&#xff0c;每轮选出当前剩余数据的最大&#xff08;最小&#xff09;放到数据 的开头&#xff0c;之后重复即可获得答案。 示例 代码 void SelectSort(OrderList *L) {RecordType t…

MySQL与其他数据库产品的比较,优势在哪里?

作为数据库管理领域的博主作家&#xff0c;我深知数据库在软件开发和数据管理中的重要性。在当今众多的数据库产品中&#xff0c;MySQL作为一种流行的开源关系型数据库管理系统&#xff0c;具有许多优势和特点。下面&#xff0c;我将通过对与其他数据库产品的比较以及MySQL的优…

Ubuntu22.04 server版本关闭DHCP,手动设置ip

在Ubuntu 22.04 中&#xff0c;网络配置已迁移到 Netplan&#xff0c;因此可以使用 Netplan 配置文件来手动设置 IP 地址并关闭 DHCP。 以下是在 Ubuntu 22.04 上手动设置 IP 地址并禁用 DHCP 的步骤&#xff1a; 打开终端&#xff0c;使用 root 权限或 sudo 执行以下命令&…

JavaScript图片处理大揭秘!掌握文件流处理方法

说在前面 &#x1f4bb;作为一名前端开发&#xff0c;我们平时也少不了对文件流数据进行处理&#xff0c;今天简单整理一下日常开发中比较常见的一些处理文件流的场景及处理方法&#xff0c;希望可以帮助到大家&#xff0c;挤出多一点的摸鱼学习时间。 常见场景 一、input框上…

计算机网络 一到二章 PPT 复习

啥币老师要隔段时间测试&#xff0c;我只能说坐胡狗吧旁边 第一章 这nm真的会考&#xff0c;我是绷不住的 这nm有五种&#xff0c;我一直以为只有三种 广播帧在后面的学习中经常遇到 虽然老师在上课的过程中并没有太过强调TCP/IP的连接和断开&#xff0c;但我必须强调一下&…

iOS--UIPickerView学习

UIPickerView 使用场景和功能UIPickerView遵循代理协议和数据源协议创建对象&#xff0c;添加代理必须实现的代理方法非必要实现的方法demo用到的其他函数提示 效果展示 使用场景和功能 UIPickerView 最常见的用途是作为选项选择器&#xff0c;允许用户从多个选项中选择一个。…

『亚马逊云科技产品测评』活动征文| 基于etcd实现服务发现

提示&#xff1a;授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 Developer Centre, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 背景 etcd 是一个分布式 Key-Value 存储系统&#xff0…

Audacity降噪消除视频中杂音

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

【嵌入式Linux开发一路清障-连载04】虚拟机VirtualBox7.0安装Ubuntu22.04后挂载Windows平台共享文件夹

虚拟机安装Ubuntu22.04后挂载Windows平台共享文件夹 障碍07-虚拟机VirtualBox7.0完装完Ubuntu22.04后&#xff0c;无法成功挂载Windows平台中共享文件夹&#xff0c;无法访问电脑中的各类重要文件&#xff0c;我该怎么办&#xff1f;一、问题的模样&#xff1a;VirtualBox7.0设…

用Metasploit进行信息收集2

基于FTP协议收集信息 1.查看ftp服务的版本信息 打开metasploit 查看ftp版本的模块&#xff0c;并进入模块 msf6 > search ftp_version msf6 > use auxiliary/scanner/ftp/ftp_version msf6 auxiliary(scanner/ftp/ftp_version) > show options 查看靶机的端口开方情…

宋仕强论道之华强北自组织和激励模式(十四)

宋仕强论道之华强北自组织和激励模式&#xff08;十四&#xff09;: 为什么一个小小深圳市华强北我宋仕强就讲这么久呢&#xff0c;听说玄奘大和尚刚出道时在洛阳的白马寺讲经&#xff0c;一个“悟”字就讲了三个月。一个事物有他的复杂性和多样性&#xff0c;从自然科学和社会…

visual studio 2022 更改字体和大小

工具--->选项 文本编辑器 输出窗口

【Openstack Train安装】五、Memcached/Etcd安装

本文介绍Memcached/Etcd安装步骤&#xff0c;Memcached/Etcd仅需在控制节点安装。 在按照本教程安装之前&#xff0c;请确保完成以下配置&#xff1a; 【Openstack Train安装】一、虚拟机创建 【Openstack Train安装】二、NTP安装 【Openstack Train安装】三、openstack安装…

处理跨域问题

这里只讨论后端对跨域支持,前端的跨域支持一般都是在测试阶段用用的,跨域还是要后端解决 跨域问题的产生:浏览器的一种安全机制-->同源策略限制 同源策略:URL中包括协议&#xff0c;域名&#xff0c;IP&#xff0c;端口都要完全相同&#xff0c;如果有一项不同&#xff0c;浏…

基于Java SSM框架+Vue实现病人跟踪治疗信息系统项目【项目源码+论文说明】

基于java的SSM框架Vue实现病人跟踪治疗信息系统演示 摘要 病人跟踪治疗信息管理系统采用B/S模式&#xff0c;促进了病人跟踪治疗信息管理系统的安全、快捷、高效的发展。传统的管理模式还处于手工处理阶段&#xff0c;管理效率极低&#xff0c;随着病人的不断增多&#xff0c;…

【开源】基于Vue+SpringBoot的智能教学资源库系统

项目编号&#xff1a; S 050 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S050&#xff0c;文末获取源码。} 项目编号&#xff1a;S050&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 课程档案模块2.3 课…

测试相关-面试高频

测试面试相关 面试 测试的具体场景 功能测试 具体的测试工具Jmeter Postman selenium pytest 怎么看待测试的潜力与挑战 软件测试是正在快速发展&#xff0c;充满挑战的领域。尽管现在许多自动化测试软件的出现使得传统手工测试的方式被代替&#xff0c;但自动化测试工具的…

Echarts 大屏注册自定义地图解析文件流报错以及坐标显示数值和地图填充以及dataV轮播数据不显示问题解决

效果图: 1、第一种方式 后台接口获取到SVG图片的文件流,postman能够正确解析出文件流,前端调用api时需要设置返回的响应格式为image/svg+xml格式,否则解析失败 拿到文件流后是这样的 <?xml version="1.0" encoding="utf-8"?> <!-- Generato…

06 # 枚举类型

一个角色判断例子 function initByRole(role) {if (role 1 || role 2) {// do sth} else if (role 3 || role 4) {// do sth} else if (role 5) {// do sth} else {// do sth} }上面的代码存在的问题&#xff1a; 可读性差&#xff1a;很难记住数字的含义可维护性差&…