MySQL-04-InnoDB存储引擎锁和加锁分析

      Latch一般称为闩锁(轻量级锁),因为其要求锁定的时间必须非常短。在InnoDB存储引擎中,latch又分为mutex(互斥量)和rwlock(读写锁)。
       Lock的对象是事务,用来锁定的是数据库中的对象,如表、页、行。并且一般lock的对象仅在事务commit或rollback后进行释放(不同的事务隔离级别释放时间可能不同)。

1-InnoDB存储引擎中的锁

共享锁(S Lock),允许事务读一行数据;
排他锁(X Lock),允许事务删除或更新一行数据

X锁和任何锁都不兼容,而S锁仅和S锁兼容  X锁和S锁都是行锁
兼容是指对同一记录(row)锁的兼容性情况

2-行锁的算法

InnoDB存储引擎有3种行锁算法,分别是:
Record Lock:单个行记录上的锁
Gap Lock:间隙锁,锁定一个范围,但不包含记录本身
Next-Key Lock:Gap Lock+ Record Lock,锁定一个范围,并且锁定记录本身
采用Next-Key Lock的锁定技术成为Next-Key Locking。其目的是为了解决幻读(phantom problem),其锁定不是单个值,而是一个范围,是谓词锁(predict lock)的一种改进。例如有一个索引有10,11,13,20这四个值,那么该索引可能被Next-Key Locking区间为(-∞,10]、(10,11]、(11,13]、(13,20]、(20,+ ∞)
InnoDB存储引擎的事务默认级别是RR,在该级别下,其采用Next-Key Locking的方式来加锁。而在RC级别下,其仅采用Record Lock。

开胃菜锁分析演示

CREATE TABLE `ta` (
  `a` int(10) NOT NULL,
  `b` int(10) NOT NULL,
  `c` int(10) NOT NULL,
  PRIMARY KEY (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into ta (a,b,c) values (1,3,4);
insert into ta (a,b,c) values (5,8,10);
insert into ta (a,b,c) values (10,12,13);

分析:表ta中有1,5,10三条记录,session1首先对a=5进行X锁定,由于a是主键,因此仅仅锁定5这个值,不是锁定范围,因此插入a=4不会阻塞。锁定由Next-Key Lock算法降级为Record Lock,提供应用的并发性。
如果a列是唯一索引,锁定由Next-Key Lock算法降级为Record Lock,提供应用的并发性

delete from ta where a=4;删除刚才插入的记录
ALTER TABLE `ta` ADD INDEX `index_b` (`b`) ;

执行如下5条语句:
(1)select * from ta where a=5 lock in share mode;
(2)insert into ta (a,b,c) values (4,7 ,44);
(3)insert into ta (a,b,c) values (6,11,44);
(4)insert into ta (a,b,c) values (6,12,44);
(5)insert into ta (a,b,c) values (20,3,44);
上面5条语句不能执行
解释:
b=8,查询到记录的主键是a=5,首先会锁定a=5的记录(X锁),所以第一条阻塞;由于索引b的值为3,8,12
该索引可能被Next-Key Locking区间为(-∞,3]、(3,8]、(8,12]、 (12,+ ∞)
虽然第二条第三条第四条第五条主键都不等于5,但是b的值在[3,12]范围内,因此都是阻塞的。

以下两条可以执行:是因为不满足a=5和b的区间[3,12]范围内。
insert into ta (a,b,c) values (100,2,44);
insert into ta (a,b,c) values (101,13,44);
 

3-加锁分析之大餐

MVCC:Snapshot Read vs Current Read
MySQL InnoDB存储引擎,实现的是基于多版本的并发控制协议——MVCC (Multi-Version Concurrency Control) (注:与MVCC相对的,是基于锁的并发控制,Lock-Based Concurrency Control)。MVCC最大的好处,相信也是耳熟能详:读不加锁,读写不冲突。在读多写少的OLTP应用中,读写不冲突是非常重要的,极大的增加了系统的并发性能,这也是为什么现阶段,几乎所有的RDBMS,都支持了MVCC。
在MVCC并发控制中,读操作可以分成两类:快照读 (snapshot read)与当前读 (current read)。快照读,读取的是记录的可见版本 (有可能是历史版本),不用加锁。当前读,读取的是记录的最新版本,并且,当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录。
在一个支持MVCC并发控制的系统中,哪些读操作是快照读?哪些操作又是当前读呢?

以MySQL InnoDB为例
快照读:简单的select操作,属于快照读,不加锁。(当然,也有例外,下面会分析)
select * from table where ?;
当前读:特殊的读操作,插入/更新/删除操作,属于当前读,需要加锁。
select * from table where ? lock in share mode;
select * from table where ? for update;
insert into table values (…);
update table set ? where ?;
delete from table where ?;
所有以上的语句,都属于当前读,读取记录的最新版本。并且,读取之后,还需要保证其他并发事务不能修改当前记录,对读取记录加锁。其中,除了第一条语句,对读取记录加S锁 (共享锁)外,其他的操作,都加的是X锁 (排它锁)。

场景准备:

SQL1:select * from t1 where id = 10;
SQL2:delete from t1 where id = 10;

SQL1:select操作均不加锁,采用的是快照读,因此在下面的讨论中就忽略了,
主要讨论SQL2:delete操作的加锁。

所有的加锁分析必须提前告知:隔离级别和表的字段的索引情况(主键,唯一索引,普通索引还是普通字段没有索引),一切不以这些前提的分析都是瞎扯,一切不以这些前提的分析都是瞎扯,一切不以这些前提的分析都是瞎扯。


3.1-RC+id主键

delete from t1 where id = 10;// 只需要将主键上,id = 10的记录加上X锁即可。

表结构:t1(id primary key,name)

结论:id是主键时,此SQL只需要在id=10这条记录上加X锁即可。

3.2-RC+id唯一索引

表结构:t1(name primary key,id unique key)

       此组合中,id是unique索引,而主键是name列。此时,加锁的情况由于组合一有所不同。由于id是unique索引,因此delete语句会选择走id列的索引进行where条件的过滤,在找到id=10的记录后,首先会将unique索引上的id=10索引记录加上X锁,同时,会根据读取到的name列,回主键索引(聚簇索引),然后将聚簇索引上的name = ‘c’ 对应的主键索引项加X锁。为什么聚簇索引上的记录也要加锁?试想一下,如果并发的一个SQL,是通过主键索引来更新:update t1 set id = 100 where name = ‘c’; 此时,如果delete语句没有将主键索引上的记录加锁,那么并发的update就会感知不到delete语句的存在,违背了同一记录上的更新/删除需要串行执行的约束。

      结论:若id列是unique列,其上有unique索引。那么SQL需要加两个X锁,一个对应于id unique索引上的id = 10的记录,另一把锁对应于聚簇索引上的[name=’c’,id=10]的记录。

3.3-RC+id非唯一索引

  表结构:t1(name primary key,id key)

       相对于组合一、二,组合三又发生了变化,隔离级别仍旧是RC不变,但是id列上的约束又降低了,id列不再唯一,只有一个普通的索引。假设delete from t1 where id = 10; 语句,仍旧选择id列上的索引进行过滤where条件,那么此时会持有哪些锁?

     

       根据此图,可以看到,首先,id列索引上,满足id = 10查询条件的记录,均已加锁。同时,这些记录对应的主键索引上的记录也都加上了锁。与组合二唯一的区别在于,组合二最多只有一个满足等值查询的记录,而组合三会将所有满足查询条件的记录都加锁。

      结论:若id列上有非唯一索引,那么对应的所有满足SQL查询条件的记录,都会被加锁。同时,这些记录在主键索引上的记录,也会被加锁。

3.4-RC+id无索引

表结构:t1(name primary key,id)

      相对于前面三个组合,这是一个比较特殊的情况。id列上没有索引,where id = 10;这个过滤条件,没法通过索引进行过滤,那么只能走全表扫描做过滤。对应于这个组合,SQL会加什么锁?或者是换句话说,全表扫描时,会加什么锁?这个答案也有很多:有人说会在表上加X锁;有人说会将聚簇索引上,选择出来的id = 10;的记录加上X锁。那么实际情况呢?请看下图:

      由于id列上没有索引,因此只能走聚簇索引,进行全部扫描。从图中可以看到,满足删除条件的记录有两条,但是,聚簇索引上所有的记录,都被加上了X锁。无论记录是否满足条件,全部被加上X锁。既不是加表锁,也不是在满足条件的记录上加行锁。

      有人可能会问?为什么不是只在满足条件的记录上加锁呢?这是由于MySQL的实现决定的。如果一个条件无法通过索引快速过滤,那么存储引擎层面就会将所有记录加锁后返回,然后由MySQL Server层进行过滤。因此也就把所有的记录,都锁上了。

      注:在实际的实现中,MySQL有一些改进,在MySQL Server过滤条件,发现不满足后,会调用unlock_row方法,把不满足条件的记录放锁 (违背了2PL的约束)。这样做,保证了最后只会持有满足条件记录上的锁,但是每条记录的加锁操作还是不能省略的。

       结论:若id列上没有索引,SQL会走聚簇索引的全扫描进行过滤,由于过滤是由MySQL Server层面进行的。因此每条记录,无论是否满足条件,都会被加上X锁。但是,为了效率考量,MySQL做了优化,对于不满足条件的记录,会在判断后放锁,最终持有的,是满足条件的记录上的锁,但是不满足条件的记录上的加锁/放锁动作不会省略。同时,优化也违背了2PL的约束。

3.5-RR+id主键

加锁同3.1

3.6-RR+id唯一索引

加锁同3.2

3.7-RR+id非唯一索引

表结构:t1(name primary key,id key)

      RC隔离级别允许幻读,而RR隔离级别,不允许存在幻读。但是在组合五、组合六中,加锁行为又是与RC下的加锁行为完全一致。那么RR隔离级别下,如何防止幻读呢?问题的答案,就在组合七中揭晓。

      组合七,Repeatable Read隔离级别,id上有一个非唯一索引,执行delete from t1 where id = 10; 假设选择id列上的索引进行条件过滤,最后的加锁行为,是怎么样的呢?

       此图,相对于组合三:[id列上非唯一锁,Read Committed]看似相同,其实却有很大的区别。最大的区别在于,这幅图中多了一个GAP锁,而且GAP锁看起来也不是加在记录上的,倒像是加载两条记录之间的位置,GAP锁有何用?

       其实这个多出来的GAP锁,就是RR隔离级别,相对于RC隔离级别,不会出现幻读的关键。确实,GAP锁锁住的位置,也不是记录本身,而是两条记录之间的GAP。所谓幻读,就是同一个事务,连续做两次当前读 (例如:select * from t1 where id = 10 for update;),那么这两次当前读返回的是完全相同的记录 (记录数量一致,记录本身也一致),第二次的当前读,不会比第一次返回更多的记录 (幻象)。

       如何保证两次当前读返回一致的记录,那就需要在第一次当前读与第二次当前读之间,其他的事务不会插入新的满足条件的记录并提交。为了实现这个功能,GAP锁应运而生。

       如图中所示,有哪些位置可以插入新的满足条件的项 (id = 10),考虑到B+树索引的有序性,满足条件的项一定是连续存放的。记录[5,e]之前,不会插入id=10的记录;[5,e]与[10,c]间可以插入[10, aa];[10,c]与[10,a]间,可以插入新的[10,bb],[10,cc]等;[10,a]与[20,b]间可以插入满足条件的[10,ee],[10,zz]等;而[20,b]之后也不会插入满足条件的记录。因此,为了保证[5,e]与[10,c]间,[10,c]与[10,a]间,[10,a]与[20,b]不会插入新的满足条件的记录,MySQL选择了用GAP锁,将这三个GAP给锁起来。

      Insert操作,如insert [10,d],首先会定位到[5,e]与[10,c]间,然后在插入前,会检查这个GAP是否已经被锁上,如果被锁上,则Insert不能插入记录。因此,通过第一遍的当前读,不仅将满足条件的记录锁上 (X锁),与组合三类似。同时还是增加3把GAP锁,将可能插入满足条件记录的3个GAP给锁上,保证后续的Insert不能插入新的id=10的记录,也就杜绝了同一事务的第二次当前读,出现幻象的情况。

有心的朋友看到这儿,可以会问:既然防止幻读,需要靠GAP锁的保护,为什么组合五、组合六,也是RR隔离级别,却不需要加GAP锁呢?

首先,这是一个好问题。其次,回答这个问题,也很简单。GAP锁的目的,是为了防止同一事务的两次当前读,出现幻读的情况。而组合五,id是主键;组合六,id是unique键,都能够保证唯一性。一个等值查询,最多只能返回一条记录,而且新的相同取值的记录,一定不会在新插入进来,因此也就避免了GAP锁的使用。

     结论:Repeatable Read隔离级别下,id列上有一个非唯一索引,对应SQL:delete from t1 where id = 10; 首先,通过id索引定位到第一条满足查询条件的记录,加记录上的X锁,加GAP上的GAP锁,然后加主键聚簇索引上的记录X锁,然后返回;然后读取下一条,重复进行。直至进行到第一条不满足条件的记录[20,b],此时,不需要加记录X锁,但是仍旧需要加GAP锁,最后返回结束。

3.8-RR+id无索引

     Repeatable Read隔离级别下的最后一种情况,id列上没有索引。此时SQL:delete from t1 where id = 10; 没有其他的路径可以选择,只能进行全表扫描。

表结构:t1(name primary key,id)

       如图,这是一个很恐怖的现象。首先,聚簇索引上的所有记录,都被加上了X锁。其次,聚簇索引每条记录间的间隙(GAP),也同时被加上了GAP锁。这个示例表,只有5条记录,一共需要5个记录锁,6个GAP锁。试想,如果表上有1000万条记录呢?

       在这种情况下,这个表上,除了不加锁的快照度,其他任何加锁的并发SQL,均不能执行,不能更新,不能删除,不能插入,全表被锁死。

       当然,跟组合四:[id无索引, Read Committed]类似,这个情况下,MySQL也做了一些优化,就是所谓的semi-consistent read。semi-consistent read开启的情况下,对于不满足查询条件的记录,MySQL会提前放锁。针对上面的这个用例,就是除了记录[a,10],[c,10]之外,所有的记录锁都会被释放,同时不加GAP锁。semi-consistent read如何触发:要么是read committed隔离级别;要么是Repeatable Read隔离级别,同时设置了 innodb_locks_unsafe_for_binlog 参数。

      结论:在Repeatable Read隔离级别下,如果进行全表扫描的当前读,那么会锁上表中的所有记录,同时会锁上聚簇索引内的所有GAP,杜绝所有的并发 更新/删除/插入 操作。当然,也可以通过触发semi-consistent read,来缓解加锁开销与并发影响,但是semi-consistent read本身也会带来其他问题,不建议使用。

3.9-Serializable

       针对前面提到的简单的SQL,最后一个情况:Serializable隔离级别。对于SQL2:delete from t1 where id = 10; 来说,Serializable隔离级别与Repeatable Read隔离级别完全一致,因此不做介绍。

      Serializable隔离级别,影响的是SQL1:select * from t1 where id = 10; 这条SQL,在RC,RR隔离级别下,都是快照读,不加锁。但是在Serializable隔离级别,SQL1会加读锁,也就是说快照读不复存在,MVCC并发控制降级为Lock-Based CC。

      结论:在MySQL/InnoDB中,所谓的读不加锁,并不适用于所有的情况,而是隔离级别相关的。Serializable隔离级别,读不加锁就不再成立,所有的读操作,都是当前读。

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

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

相关文章

实验题【网关设置+VRRP+静态路由+OSPF】(H3C模拟器)

嘿,这里是目录! ⭐ H3C模拟器资源链接1. 实验示意图2. 要求和考核目标3. 当前配置3.1 PC1、PC2、PC3、PC4和PC5配置3.2 SW配置3.2.1 SW2配置3.2.2 SW3配置3.2.3 SW4配置3.2.4 SW1配置 3.2. R配置3.2.1 R1配置3.2.2 R2配置 ⭐ H3C模拟器资源链接 H3C网络…

Windows下安装MySQL

几年前学习mycat中间件的时候在window机器上安装过MySql,但是由于电脑配置不高,同时打开Mysql服务,idea、SQlyog等软件非常卡,再加上SQLyog和MySQL版本不兼容导致登录不上,于是把它卸载了。最近做练习需要,…

Qt 软件调试(二)使用dump捕获崩溃信息

Qt应用程序异常崩溃该怎么办&#xff0c;生成dump文件再回溯分析&#xff0c;可以快速且准确的帮助我们定位到崩溃的点。那么&#xff0c;本章我们分享下如何在Qt中生成dump文件。 一、使用minudump捕获崩溃信息 #include <QCoreApplication> #include <QDir> #i…

用Python写一个浏览器集群框架

更多Python学习内容&#xff1a;ipengtao.com 在分布式爬虫和大规模数据采集的场景中&#xff0c;使用浏览器集群是一种有效的方式&#xff0c;可以提高数据采集的速度和效率。本文将介绍如何用Python编写一个简单但强大的浏览器集群框架&#xff0c;以应对需要使用多个浏览器实…

原生小程序图表

原生小程序使用图表 话不多说直接进入正题 官方文档: https://www.ucharts.cn/v2/#/ 下载文件 首先去gitee上把文件下载到自己的项目中 https://gitee.com/uCharts/uCharts 找到微信小程序和里面的组件 把里面src下的文件全部下载下来放入自己项目中 项目文件 新建文件…

使用Linux JumpServer堡垒机本地部署与远程访问

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;网络奇遇记、Cpolar杂谈 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. 安装Jump server二. 本地访问jump server三. 安装 cpolar内网穿透软件四. 配…

【面试HOT200】滑动窗口篇

系列综述&#xff1a; &#x1f49e;目的&#xff1a;本系列是个人整理为了秋招面试的&#xff0c;整理期间苛求每个知识点&#xff0c;平衡理解简易度与深入程度。 &#x1f970;来源&#xff1a;材料主要源于【CodeTopHot200】进行的&#xff0c;每个知识点的修正和深入主要参…

linux shell操作 - 05 IO 模型

文章目录 流IO模型阻塞IO非阻塞IOIO多路复用异步IO网络IO模型 流 可以进行IO&#xff08;input输入、output输出&#xff09;操作的内核对象&#xff1b;如文件、管道、socket…流的入口是fd (file descriptor)&#xff1b; IO模型 阻塞IO&#xff0c; 一直等待&#xff0c;…

LLMLingua:集成LlamaIndex,对提示进行压缩,提供大语言模型的高效推理

大型语言模型(llm)的出现刺激了多个领域的创新。但是在思维链(CoT)提示和情境学习(ICL)等策略的驱动下&#xff0c;提示的复杂性不断增加&#xff0c;这给计算带来了挑战。这些冗长的提示需要大量的资源来进行推理&#xff0c;因此需要高效的解决方案&#xff0c;本文将介绍LLM…

分布式篇---第五篇

系列文章目录 文章目录 系列文章目录前言一、你知道哪些限流算法?二、说说什么是计数器(固定窗口)算法三、说说什么是滑动窗口算法前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去…

golang panic关键词执行原理与代码分析

使用的go版本为 go1.21.2 首先我们写一个简单的panic调度与捕获代码 package mainfunc main() {defer func() {recover()}()panic("panic test") }通过go build -gcflags -S main.go获取到对应的汇编代码 可以看到当我们调度panic时&#xff0c;Go的编译器会将这段…

利用Nginx与php处理方式不同绕过Nginx_host实现SQL注入

目录 首先需要搭建环境 nginxphpmysql环境&#xff1a; 搭建网站 FILTER_VALIDATE_EMAIL 绕过 方法1&#xff1a;冒号号分割host字段 方法2&#xff1a;冒号号分割host字段 方法3&#xff1a;SNI扩展绕过 首先需要搭建环境 nginxphpmysql环境&#xff1a; php安装包&a…

具有150KHz固定频率的PWM控制降压型稳压电路芯片D2504,可兼容型号XL4001

D2504是一块具有150KHz固定频率的PWM控制降压型稳压电路&#xff0c;具有高转换效率、2A负 载能力和优异的负载调整率和电压线性度。 主要特点&#xff1a; ● 输入电压范围: 4.5~40V ● 可调输出电压: 1.235~37V ● 最小Drop电压1 5V2A ● 150K 固…

迈巴赫S480升级主动式氛围灯 浪漫婉转的气氛

主动式氛围灯有263个可多色渐变的LED光源&#xff0c;营造出全情沉浸的动态光影氛围。结合智能驾驶辅助系统&#xff0c;可在转向或检测到危险时&#xff0c;予以红色环境光提示&#xff0c;令光影艺术彰显智能魅力。配件有6个氛围灯&#xff0c;1个电脑模块。 1、气候&#xf…

超分辨率重建

意义 客观世界的场景含有丰富多彩的信息&#xff0c;但是由于受到硬件设备的成像条件和成像方式的限制&#xff0c;难以获得原始场景中的所有信息。而且&#xff0c;硬件设备分辨率的限制会不可避免地使图像丢失某些高频细节信息。在当今信息迅猛发展的时代&#xff0c;在卫星…

数据结构与算法编程题21

判别两棵树是否相等。 #define _CRT_SECURE_NO_WARNINGS#include <iostream> using namespace std;typedef char ElemType; #define ERROR 0 #define OK 1typedef struct BiNode {ElemType data;BiNode* lchild, * rchild; }BiNode, * BiTree;bool Create_tree(BiTree&a…

python之pyqt专栏3-QT Designer

从前面两篇文章python之pyqt专栏1-环境搭建与python之pyqt专栏2-项目文件解析&#xff0c;我们对QT Designer有基础的认识。 QT Designer用来创建UI界面&#xff0c;保存的文件是"xxx.ui"文件&#xff0c;"xxx.ui"可以被pyuic转换为"xxx.py",而&…

html table样式的设计 表格边框修饰

<!DOCTYPE html> <html> <head> <meta http-equiv"Content-Type" content"text/html; charsetutf-8" /> <title>今日小说排行榜</title> <style> table {border-collapse: collapse;border: 4px double red; /*…

VC++彻底理解链接器:四,重定位

重定位 程序的运行过程就是CPU不断的从内存中取出指令然后执行执行的过程&#xff0c;对于函数调用来说比如我们在C/C语言中调用简单的加法函数add&#xff0c;其对应的汇编指令可能是这样的: call 0x4004fd 其中0x4004fd即为函数add在内存中的地址&#xff0c;当CPU执行这条…

2023大模型安全解决方案白皮书

今天分享的是大模型系列深度研究报告&#xff1a;《2023大模型安全解决方案白皮书》。 &#xff08;报告出品方&#xff1a;百度安全&#xff09; 报告共计&#xff1a;60页 前言 在当今迅速发展的数字化时代&#xff0c;人工智能技术正引领着科技创新的浪潮而其中的大模型…