最新要闻
- 微信iOS版拍照“大升级”:终于支持微距拍摄
- 当前快看:支付宝新增“极速模式”:自动收起首页推荐 更清爽了
- 当前关注:连花清瘟可致肝损伤肝衰竭?药企回应:严重误导
- 腾讯:2022年游戏盗号量上涨300% DDoS攻击全行业最高
- 花费13亿、飞了540万公里:韩国探测器终于进入月球轨道
- 天天观察:6GHz就这?!Intel i9-13900KS跑分勉强提升5%
- 微速讯:油管上最爆火的恐怖游戏:被托马斯小火车追杀
- 电池供电不插线:世界首款真无线电视将在CES亮相
- 快看:3D领域大神约翰·卡马克宣布彻底离开Meta:称其效率低到无法忍受
- 男子按导航开车到冰冻江面 一头栽入松花江
- 天天讯息:索尼要爆发了!明年有望推出新款PS5:独占大作护航
- 每日播报!疯狂裁员后 曝马斯克正为推特寻找新买家
- 环球最资讯丨“三连跌” 明晚油价迎年内“最后一调”:92号汽油或回归7元时代
- 世界头条:豆瓣8.3分!《阿凡达:水之道》票房破3亿:会是救市之作吗
- 全球热推荐:低温蓝色预警!这些地方最低温较历史同期低7℃以上:南方网友瑟瑟发抖
- 克罗地亚2-1战胜摩洛哥夺世界杯季军 37岁莫德里奇告别世界杯赛场
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
焦点热议:核心面试题:MVCC、间隙锁、Undo Log链、表级锁、行级锁、页级锁、共享锁、排它锁、记录锁等等
文章很长,而且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版为您奉上珍贵的学习资源 :
免费赠送 :《尼恩Java面试宝典》持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备免费赠送 经典图书:《Java高并发核心编程(卷1)加强版》面试必备 + 大厂必备 +涨薪必备 加尼恩免费领免费赠送 经典图书:《Java高并发核心编程(卷2)加强版》面试必备 + 大厂必备 +涨薪必备 加尼恩免费领免费赠送 经典图书:《Java高并发核心编程(卷3)加强版》面试必备 + 大厂必备 +涨薪必备 加尼恩免费领免费赠送 经典图书:《尼恩Java面试宝典 最新版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领免费赠送 资源宝库: Java 必备 百度网盘资源大合集 价值>10000元 加尼恩领取
(资料图)
注:本文以 PDF 持续更新,最新尼恩 架构笔记、面试题 的PDF文件,请从下面的链接获取:语雀 或者 码云
聊聊:MVCC 机制如何解决幻读?
在RR的隔离级别下,Innodb使用MVCC和 next-key locks(行锁和间隙锁的组合)解决幻读,
- MVCC解决的是普通读(快照读)的幻读,
- next-key locks解决的是当前读情况下的幻读。
所以,来看看 MVCC 机制如何普通读(快照读)的幻读?
了解了这些概念之后,我们来看下当查询一条记录的时候,系统如何通过MVCC找到它:
- 首先获取事务自己的版本号,也就是事务 ID;
- 获取 ReadView 读试图;
- 查询得到的数据,然后与 ReadView 中的事务版本号进行比较;
- 如果不符合 ReadView 规则,就需要从 Undo Log 中获取历史快照;
- 最后返回符合规则的数据。
不同的隔离级别,或者的Read View 数量不同:
- 在隔离级别为RC 读已提交(Read Committed)时,一个事务中的每一次 SELECT 查询都会重新获取一次Read View。如表所示:
注意,此时同样的查询语句都会重新获取一次 Read View,这时如果 Read View 不同,就可能产生 不可重复读或者幻读的情况。
- 当隔离级别为 RR 可重复读的时候,就避免了不可重复读,这是因为一个事务只在第一次 SELECT 的时候会 获取一次 Read View,而后面所有的 SELECT 都会复用这个 Read View,如下表所示:
举例说明 :READ COMMITTED 隔离级别下
READ COMMITTED :每次读取数据前都生成一个ReadView。
现在有两个 事务id 分别为 10 、 20 的事务在执行:
# Transaction 10 BEGIN;UPDATE student SET name="李四" WHERE id=1; UPDATE student SET name="王五" WHERE id=1; # Transaction 20 BEGIN; # 更新了一些别的表的记录 ...
此刻,表student 中 id 为 1 的记录得到的版本链表如下所示:
说明:
事务执行过程中,只有在第一次真正修改记录时(比如使用INSERT、DELETE、UPDATE语句),才会被分配一个单独的事务id,这个事务id是递增的。所
以我们才在事务2中更新一些别的表的记录,目的是让它分配事务id。
假设现在有一个使用 READ COMMITTED 隔离级别的事务开始执行:
# 使用READ COMMITTED隔离级别的事务 BEGIN;# SELECT1:Transaction 10、20未提交SELECT * FROM student WHERE id = 1; # 得到的列name的值为"张三"
之后,我们把 事务id 为 10 的事务提交一下:
# Transaction 10 BEGIN; UPDATE student SET name="李四" WHERE id=1; UPDATE student SET name="王五" WHERE id=1;COMMIT;
然后再到 事务id 为 20 的事务中更新一下表 student 中 id 为 1 的记录:
# Transaction 20BEGIN; # 更新了一些别的表的记录 ... UPDATE student SET name="钱七" WHERE id=1; UPDATE student SET name="宋八" WHERE id=1;
此刻,表student中 id 为 1 的记录的版本链就长这样:
然后再到刚才使用 READ COMMITTED 隔离级别的事务中,继续查找这个 id 为 1 的记录,如下:
# 使用READ COMMITTED隔离级别的事务 BEGIN;# SELECT1:Transaction 10、20均未提交SELECT * FROM student WHERE id = 1;# 得到的列name的值为"张三"# SELECT2:Transaction 10提交,Transaction 20未提交 SELECT * FROM student WHERE id = 1; # 得到的列name的值为"王五"
这个SELECT2的执行过程如下:
步骤1∶在执行SELECT语句时会又会单独生成一个ReadView,该ReadView的trx_ids列表的内容就是[20],up_limit_id为20,low_limit_id为21, creator_trx_id为0。
步骤2∶从版本链中挑选可见的记录,从图中看出,最新版本的列name的内容是‘宋八’,该版本的tr×_id值为20,在trx_ids列表内,所以不符合可见性要求,根据roll.pointer跳到下一个版本。
步骤3∶下一个版本的列name的内容是’钱七’,该版本的trx_id值为20,也在trx_ids列表内,所以也不符合要求,继续跳到下一个版本。
步骤4∶下一个版本的列name的内容是’王五’,该版本的trx_id值为10,小于ReadView中的up_limit.id值20,所以这个版本是符合要求的,最后返回给用户的版本就是这条列name为’王五’的记录。
以此类推,如果之后事务id为20的记录也提交了,再次在使用READ CONMMITTED隔离级别的事务中查询表student中id值为1的记录时,得到的结果就是‘宋八’了,具体流程我们就不分析了。
REPEATABLE READ隔离级别下
使用 REPEATABLE READ 隔离级别的事务来说,只会在第一次执行查询语句时生成一个 ReadView ,之 后的查询就不会重复生成了。
比如,系统里有两个 事务id 分别为 10 、 20 的事务在执行:
# Transaction 10 BEGIN;UPDATE student SET name="李四" WHERE id=1; UPDATE student SET name="王五" WHERE id=1;# Transaction 20 BEGIN; # 更新了一些别的表的记录 ...
此刻,表student 中 id 为 1 的记录得到的版本链表如下所示:
假设现在有一个使用 REPEATABLE READ 隔离级别的事务开始执行:
# 使用REPEATABLE READ隔离级别的事务 BEGIN;# SELECT1:Transaction 10、20未提交SELECT * FROM student WHERE id = 1; # 得到的列name的值为"张三"
之后,我们把 事务id 为 10 的事务提交一下,就像这样:
# Transaction 10BEGIN; UPDATE student SET name="李四" WHERE id=1;UPDATE student SET name="王五" WHERE id=1;COMMIT;
然后再到 事务id 为 20 的事务中更新一下表 student 中 id 为 1 的记录:
# Transaction 20 BEGIN;# 更新了一些别的表的记录 ... UPDATE student SET name="钱七" WHERE id=1; UPDATE student SET name="宋八" WHERE id=1;
此刻,表student 中 id 为 1 的记录的版本链长这样:
然后再到刚才使用 REPEATABLE READ 隔离级别的事务中继续查找这个 id 为 1 的记录,如下:
# 使用REPEATABLE READ隔离级别的事务 BEGIN; # SELECT1:Transaction 10、20均未提交SELECT * FROM student WHERE id = 1; # 得到的列name的值为"张三"# SELECT2:Transaction 10提交,Transaction 20未提交 SELECT * FROM student WHERE id = 1; # 得到的列name的值仍为"张三"
这个SELECT2的执行过程如下:
步骤1∶因为当前事务的隔离级别为REPEATABLE READ,而之前在执行SELECT1时已经生成过ReadView了,所以此时直接复用之前的ReadView,之前的ReadView的trx_ids列表的内容就是[10,20],up_limit_id为10, low_limit_id为21 , creator_trx_id为0。
步骤2∶然后从版本链中挑选可见的记录,从图中可以看出,最新版本的列name的内容是’宋八’trx_id值为20,在trx_ids列表内,所以不符合可见性要求,根据roll_pointer跳到下一个版本。
步骤3∶下一个版本的列name的内容是’钱七’,该版本的trx_id值为20,也在trx_ids列表内合要求,继续跳到下一个版本。
步骤4∶下一个版本的列name的内容是’王五’,该版本的trx_id值为10,而trx_ids列表中是包含值为10的事务id的,所以该版本也不符合要求,同理下一个列name的内容是’李四’的版本也不符合要求。继续跳到下个版本。
步聚5∶下一个版本的列name的内容是’张三’,该版本的trx_id值为80,小于Readview中的up_limit_id值10,所以这个版本是符合要求的,最后返回给用户的版本就是这条列c为‘张三’的记录。
两次SELECT查询得到的结果是重复的,记录的列c值都是’张三’,这就是可重复读的含义。
如果我们之后再把事务id为20的记录提交了,然后再到刚才使用REPEATABLE READ隔离级刷的事务中继续查找这个id为1的记录,得到的结果还是’张三’,具体执行过程大家可以自己分析一下。
如何解决幻读
接下来说明InnoDB 是如何解决幻读的。
假设现在表 student 中只有一条数据,数据内容中,主键 id=1,隐藏的 trx_id=10,它的 undo log 如下图 所示。
假设现在有事务 A 和事务 B 并发执行, 事务 A 的事务 id 为 20 , 事务 B 的事务 id 为 30 。
步骤1:事务 A 开始第一次查询数据,查询的 SQL 语句如下。
select * from student where id >= 1;
在开始查询之前,MySQL 会为事务 A 产生一个 ReadView,此时 ReadView 的内容如下: trx_ids= [20,30] , up_limit_id=20 , low_limit_id=31 , creator_trx_id=20 。
由于此时表 student 中只有一条数据,且符合 where id>=1 条件,因此会查询出来。
然后根据 ReadView机制,发现该行数据的trx_id=10,小于事务 A 的 ReadView 里 up_limit_id,这表示这条数据是事务 A 开 启之前,其他事务就已经提交了的数据,因此事务 A 可以读取到。
结论:事务 A 的第一次查询,能读取到一条数据,id=1。
步骤2:接着事务 B(trx_id=30),往表 student 中新插入两条数据,并提交事务。
insert into student(id,name) values(2,"李四"); insert into student(id,name) values(3,"王五");
此时表student 中就有三条数据了,对应的 undo 如下图所示:
步骤3:接着事务 A 开启第二次查询,根据可重复读隔离级别的规则,此时事务 A 并不会再重新生成ReadView。此时表 student 中的 3 条数据都满足 where id>=1 的条件,因此会先查出来。然后根据ReadView 机制,判断每条数据是不是都可以被事务 A 看到。
1)首先 id=1 的这条数据,前面已经说过了,可以被事务 A 看到。
2)然后是 id=2 的数据,它的 trx_id=30,此时事务 A 发现,这个值处于 up_limit_id 和 low_limit_id 之 间,因此还需要再判断 30 是否处于 trx_ids 数组内。由于事务 A 的 trx_ids=[20,30],因此在数组内,这表 示 id=2 的这条数据是与事务 A 在同一时刻启动的其他事务提交的,所以这条数据不能让事务 A 看到。
3)同理,id=3 的这条数据,trx_id 也为 30,因此也不能被事务 A 看见。
结论:最终事务 A 的第二次查询,只能查询出 id=1 的这条数据。这和事务 A 的第一次查询的结果是一样 的,因此没有出现幻读现象,所以说在 MySQL 的可重复读隔离级别下,不存在幻读问题。
总结
这里介绍了 MVCC 在 READ COMMITTD 、 REPEATABLE READ 这两种隔离级别的事务在执行快照读操作时 访问记录的版本链的过程。这样使不同事务的 读-写 、 写-读 操作并发执行,从而提升系统性能。
核心点在于 ReadView 的原理, READ COMMITTD 、 REPEATABLE READ 这两个隔离级别的一个很大不同 就是生成ReadView的时机不同:
READ COMMITTD 在每一次进行普通SELECT操作前都会生成一个ReadView
REPEATABLE READ 只在第一次进行普通SELECT操作前生成一个ReadView,之后的查询操作都重复 使用这个ReadView就好了。
通过MVCC我们可以解决:
1.读写之间阻塞的问题。
通过MVcc可以让读写互相不阻塞,即读不阻塞写,写不阻塞读,这样就可以提升事务并发处理能力。
2.降低了死锁的概率。
这是因为MVCC采用了乐观锁的方式,读取数据时并不需要加锁,对于写操作,也只锁定必要的行。
3.解次快照读的问题。
当我们查询数据库在某个时间点的快照时,只能看到这个时间点之前事务提交更新的结果,而不能看到这个时间点之后事务提交的更新结果。
聊聊:MySQL间隙锁,如何解决幻读?
在RR的隔离级别下,Innodb使用MVCC和 next-key locks(行锁和间隙锁的组合)解决幻读,
- MVCC解决的是普通读(快照读)的幻读,
- next-key locks解决的是当前读情况下的幻读。
MySQL间隙锁 + 记录锁 ,组合起来,解决的是当前读情况下的幻读
间隙锁
InnoDB支持三种锁定方式:
- 记录锁(Record Lock):锁直接加在索引记录上面。
- 间隙锁(Gap Lock):锁加在不存在的空闲空间,可以是两个索引记录之间,也可能是第一个索引记录之前或最后一个索引之后的空间。
- Next-Key Lock:记录锁与间隙锁组合起来用就叫做Next-Key Lock。
间隙锁:当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,
InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(Gap Lock锁)。
举例来说,假如user表中只有101条记录,其empid的值分别是 1,2,…,100,101,下面的SQL:
select * from user where user_id > 100 for update;
是一个范围条件的检索,InnoDB不仅会对符合条件的user_id值为101的记录加锁,也会对user_id大于101(这些记录并不存在)的“间隙”加锁。
产生幻读的原因是,行锁只能锁住行,但是新插入记录这个动作,要更新的是记录之间的“间隙”。
因此,为了解决幻读问题,InnoDB 只好引入新的锁,也就是间隙锁 (Gap Lock)。
InnoDB使用间隙锁的目的,一方面是为了防止幻读,以满足相关隔离级别的要求,
对于上面的例子,要是不使用间隙锁,如果其他事务插入了user_id大于100的任何记录,那么本事务如果再次执行上述语句,就会发生幻读;
另外一方面,是为了满足其恢复和复制的需要。
快照读和当前读
快照读(历史数据)-mvcc
innodb的默认事务隔离级别是rr(可重复读)。它的实现技术是mvcc(MVCC只在读提交可重复读两种隔离级别下工作)。
基于版本的控制协议。该技术不仅可以保证innodb的可重复读,而且可以防止幻读。
但是它防止的是快照读,也就是读取的数据虽然是一致的,但是数据是历史数据。
快照读对应的sql 语法:简单的select操作(不包括 select ... lock in share mode, select ... for update)
当前读(最新数据)-next-key lock
如何做到保证数据是一致的(也就是一个事务,其内部读取对应某一个数据的时候,数据都是一样的),同时读取的数据是最新的数据。
innodb提供了next-key lock,也就是结合gap锁与行锁,达到最终目的。
当前读(最新数据)对应的sql 语法:
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 ?;
特殊的读操作,插入/更新/删除操作,属于当前读,需要加锁。
总之:
在RR级别下,快照读是通过MVCC(多版本控制)和undo log来实现的,当前读是通过加record lock(记录锁)和gap lock(间隙锁)来实现的。
聊聊:什么是表级锁、行级锁、页级锁?
数据库锁定机制简单来说,就是数据库为了保证数据的一致性,而使各种共享资源在被并发访问变得有序所设计的一种规则。
MySQL数据库由于其自身架构的特点,存在多种数据存储引擎,每种存储引擎的锁定机制都是为各自所面对的特定场景而优化设计,所以各存储引擎的锁定机制也有较大区别。
MySQL各存储引擎使用了三种类型(级别)的锁定机制:表级锁定
,行级锁定
和页级锁定
。
1、表级锁
表级别的锁定是MySQL各存储引擎中最大颗粒度的锁定机制。该锁定机制最大的特点是实现逻辑非常简单,带来的系统负面影响最小。所以获取锁和释放锁的速度很快。
当然,锁定颗粒度大所带来最大的负面影响就是出现锁定资源争用的概率也会最高,致使并发度大打折扣。
使用表级锁定的主要是MyISAM,MEMORY,CSV等一些非事务性存储引擎。
2、行级锁
行级锁定最大的特点就是锁定对象的颗粒度很小,由于锁定颗粒度很小,所以发生锁定资源争用的概率也最小,能够给予应用程序尽可能大的并发处理能力而提高一些需要高并发应用系统的整体性能。
虽然能够在并发处理能力上面有较大的优势,但是行级锁定也因此带来了不少弊端。
由于锁定资源的颗粒度很小,所以每次获取锁和释放锁需要做的事情也更多,带来的消耗自然也就更大了。此外,行级锁定也最容易发生死锁。
使用行级锁定的主要是InnoDB存储引擎
。
3、页级锁
页级锁定是MySQL中比较独特的一种锁定级别。
页级锁定的特点是锁定颗粒度介于行级锁定与表级锁之间,所以获取锁定所需要的资源开销,以及所能提供的并发处理能力也同样是介于上面二者之间。
使用页级锁定的主要是BerkeleyDB存储引擎。
4、总结
总的来说,MySQL这3种锁的特性可大致归纳如下:
表级锁
:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低;
行级锁
:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高;
页面锁
:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
聊聊:什么是共享锁、排它锁?
InnoDB 实现了标准的行级锁,包括两种:
- 共享锁(简称 s 锁)、
- 排它锁(简称 x 锁)。
对于共享锁而言,对当前行加共享锁,不会阻塞其他事务对同一行的读请求,但会阻塞对同一行的写请求。
只有当读锁释放后,才会执行其它事物的写操作。
对于排它锁而言,会阻塞其他事务对同一行的读和写操作,只有当写锁释放后,才会执行其它事务的读写操作。
简而言之,就是:
- 读锁会阻塞写(X),但是不会堵塞读(S)
- 而写锁则会把读(S)和写(X)都堵塞
对于InnoDB 在RR(MySQL默认隔离级别) 而言,对于 update、delete 和 insert 语句, 会自动给涉及数据集加排它锁(X);
对于普通 select 语句,innodb 不会加任何锁。
如果想在select操作的时候加上 S锁 或者 X锁,需要我们手动加锁。
-- 加共享锁(S)select * from table_name where ... lock in share mode-- 加排它锁(X)select * from table_name where ... for update
读的时候,用 select... in share mode 获得共享锁,
主要用在需要数据依存关系时来确认某行记录是否存在,并确保没有人对这个记录进行 update 或者 delete 操作。
读的时候,用 select... for update获得排他锁
但是如果当前事务也需要对该记录进行更新操作, 则有可能造成死锁,对于锁定行记录后需要进行更新操作的应用,应该使用 select... for update方式获得排他锁。
聊聊:什么是记录锁(Record Locks)?
记录锁其实很好理解,对表中的记录加锁,叫做记录锁,简称行锁。
比如
SELECT * FROM `test` WHERE `id`=1 FOR UPDATE;
它会在 id=1 的记录上加上记录锁,以阻止其他事务插入,更新,删除 id=1 这一行。
需要注意的是:
- id 列必须为唯一索引列或主键列,否则上述语句加的锁就会变成临键锁(有关临键锁下面会讲)。
- 同时查询语句必须为精准匹配(=),不能为 >、<、like等,否则也会退化成临键锁。
其他实现
在通过 主键索引与 唯一索引对数据行进行 UPDATE 操作时,也会对该行数据加记录锁:
-- id 列为主键列或唯一索引列 UPDATE SET age = 50 WHERE id = 1;
记录锁是锁住记录,锁住索引记录,而不是真正的数据记录.
如果要锁的列没有索引,进行全表记录加锁
记录锁也是排它(X)锁, 所以会阻塞其他事务对其插入、更新、删除。
聊聊:什么是 间隙锁(Gap Locks)?
间隙锁 是 Innodb 在 RR(可重复读) 隔离级别下为了解决 幻读问题时引入的锁机制。
间隙锁是innodb中行锁的一种。
请务必牢记:使用间隙锁锁住的是一个区间,而不仅仅是这个区间中的每一条数据。
举例来说,假如emp表中只有101条记录,其empid的值分别是1,2,...,100,101,下面的SQL:
SELECT * FROM emp WHERE empid > 100 FOR UPDATE
当我们用条件检索数据,并请求共享或排他锁时,InnoDB不仅会对符合条件的empid值为101的记录加锁,也会对empid大于101(这些记录并不存在)的“间隙”加锁。
这个时候如果你插入empid等于102的数据的,如果那边事物还没有提交,那你就会处于等待状态,无法插入数据。
有关间隙锁所需讲的东西还是蛮多的,我会单独写一篇文章来分析间隙锁,并在文章中附上完整的示例。
聊聊:什么是临键锁(Next-Key Locks)?
Next-key锁是记录锁和间隙锁的组合,它指的是加在某条记录以及这条记录前面间隙上的锁。
也可以理解为一种特殊的间隙锁。
通过临建锁可以解决 幻读 的问题。
每个数据行上的非唯一索引列上都会存在一把临键锁,当某个事务持有该数据行的临键锁时,会锁住一段左开右闭区间的数据。
需要强调的一点是,InnoDB 中行级锁是基于索引实现的。
临键锁只与 非唯一索引列 有关,在 唯一索引列(包括主键列)上不存在临键锁。
假设有如下表:
id主键, age 普通索引
该表中 age 列潜在的临键锁有:(-∞, 10],(10, 24],(24, 32],(32, 45],(45, +∞],
在事务 A 中执行如下命令:
-- 根据非唯一索引列 UPDATE 某条记录 UPDATE table SET name = Vladimir WHERE age = 24; -- 或根据非唯一索引列 锁住某条记录 SELECT * FROM table WHERE age = 24 FOR UPDATE;
不管执行了上述 SQL 中的哪一句,之后如果在事务 B 中执行以下命令,则该命令会被阻塞:
INSERT INTO table VALUES(100, 26, "tianqi");
很明显,事务 A 在对 age 为 24 的列进行 UPDATE 操作的同时,也获取了 (24, 32] 这个区间内的临键锁。
这里对 记录锁、间隙锁、临键锁做一个总结
- InnoDB中的行锁的实现依赖于索引,一旦某个加锁操作没有使用到索引,那么该锁就会退化为
表锁
。 - 记录锁存在于包括主键索引在内的唯一索引中,锁定单条索引记录。
- 间隙锁存在于非唯一索引中,锁定开区间范围内的一段间隔。
- 临键锁存在于非唯一索引中,该类型的每条记录的索引上都存在这种锁,它是一种特殊的间隙锁,锁定一段左开右闭的索引区间。
聊聊:什么是意向锁?
意向锁又分为 意向共享锁(IS)
和 意向排他锁(IX)
- 意向共享(IS)锁:事务有意向对表中的某些行加共享锁(S锁)
-- 事务要获取某些行的 S 锁,必须先获得表的 IS 锁。 SELECT column FROM table ... LOCK IN SHARE MODE;
- 意向排他(IX)锁:事务有意向对表中的某些行加排他锁(X锁)
-- 事务要获取某些行的 X 锁,必须先获得表的 IX 锁。 SELECT column FROM table ... FOR UPDATE;
首先我们要明白四点
- 意向共享锁(IS)和 意向排他锁(IX)都是表锁。
- 意向锁是一种 不与行级锁冲突的表级锁,这一点非常重要。
- 意向锁是 InnoDB 自动加的, 不需用户干预。
- 意向锁是在 InnoDB 下存在的内部锁,对于MyISAM 而言 没有意向锁之说。
这里就会有疑惑,既然前面已经有了共享锁(S锁)、排它锁(X锁)。
那么为什么需要引入意向锁呢?它能解决什么问题呢?
我们可以理解 意向锁 存在的目的就是 为了让
InnoDB 中的行锁和表锁更高效的共存
。
为什么这么说,我们来举一个例子。
举例
下面有一张表 InnoDB RR隔离级别 id是主键
事务 A 获取了某一行的排他锁,并未提交:
SELECT * FROM users WHERE id = 6 FOR UPDATE;
事务 B 想要获取users表的表锁:
LOCK TABLES users READ;
因为共享锁与排他锁互斥,所以事务 B 在视图对 users 表加共享锁的时候,必须保证:
- 当前没有其他事务持有 users 表的排他锁。
- 当前没有其他事务持有 users 表中任意一行的排他锁 。
为了检测是否满足第二个条件,事务 B 必须在确保users表不存在任何排他锁的前提下,去检测表中的每一行是否存在排他锁。很明显这是一个效率很差的做法,但是有了意向锁之后,情况就不一样了:事务B只要看表上有没有
意向共享锁,有则说明表中有些行被共享行锁锁住了,因此,事务B申请表的写锁会被阻塞。这样是不是就高效多了。
这也解释就应该清楚,为什么有意向锁这个东西存在了。
我们可以举个生活中的例子,再来理解下为什么需要存在意向锁。
打个比方,就像有个游乐场,很多小朋友进去玩,看门大爷如果要下班锁游乐场的门(加表锁),他必须确保每个角落都要去检查一遍,确保每个小朋友都离开了(释放行锁),才可以锁门。
假设锁门是件频繁发生的事情,大爷就会非常崩溃。那大爷想了一个办法,每个小朋友进入,就把自己的名字写在本子上,小朋友离开,就把自己的名字划掉,那大爷就能方便掌握有没有小朋友在游乐场里,不必每个角落都去寻找一遍。
例子中的“小本子”,就是意向锁,他记录的信息并不精细,他只是提醒大爷,有人在屋里。
这里我们再来看下 共享(S)锁、排他(X)锁、意向共享锁(IS)、意向排他锁(IX)的兼容性
可以看出 意向锁之间是互相兼容的.那你存在的意义是啥?
意向锁不会为难意向锁。也不会为难行级排他(X)/共享(X)锁,它的存在是为难表级
排他(X)/共享(X)锁。
注意
这里的排他(X)/共享(S)锁指的都是表锁!意向锁不会与行级的共享/排他锁互斥!行级别的X和S按照上面的兼容性规则即可。
意向锁与意向锁之间永远是兼容的,因为当你不论加行级的X锁或S锁,都会自动获取表级的IX锁或者IS锁。也就是你有10个事务,对不同的10行加了行级X锁,那么这个时候就存在10个IX锁。
这10IX存在的目的是啥呢,就是假如这个时候有个事务,想对整个表加排它X锁,那它不需要遍历每一行是否存在S或X锁,而是看有没有存在意向锁,只要存在一个意向锁,那这个事务就加不了表级排它X锁,要等上面10个IX全部释放才行。
聊聊:什么是插入意向锁?
在讲解插入意向锁之前,先来思考一个问题
下面有张表 id主键,age普通索引
首先事务 A插入了一行数据,并且没有 commit:
INSERT INTO users SELECT 4, "Bill", 15;
随后事务 B试图插入一行数据:
INSERT INTO users SELECT 5, "Louis", 16;
请问:
1、事务A使用了什么锁?
2、 事务 B是否会被事务 A阻塞?
插入意向锁是在插入一条记录行前,由 INSERT操作产生的一种间隙锁
。
该锁用以表示插入意向,当多个事务在同一区间(gap)插入位置不同的多条数据时,事务之间不需要互相等待。
假设存在两条值分别为 4 和 7 的记录,两个不同的事务分别试图插入值为 5 和 6 的两条记录,每个事务在获取插入行上独占的(排他)锁前,都会获取(4,7)之间的间隙锁,但是因为数据行之间并不冲突,所以两个事务之间
并不会产生冲突(阻塞等待)。
总结来说,插入意向锁的特性可以分成两部分:
- 插入意向锁是一种特殊的间隙锁—— 间隙锁可以锁定开区间内的部分记录。
- 插入意向锁之间互不排斥,所以即使多个事务在同一区间插入多条记录,只要记录本身(主键、唯一索引)不冲突,那么事务之间就不会出现冲突等待。
需要强调的是,虽然插入意向锁中含有意向锁三个字,但是它并不属于意向锁而属于间隙锁,因为意向锁是表锁而插入意向锁是行锁
。
现在我们可以回答开头的问题了:
1、使用插入意向锁与记录锁。
2、事务 A不会阻塞事务 B。
为什么不用间隙锁
如果只是使用普通的间隙锁会怎么样呢?
我们在看事务A,其实它一共获取了3把锁
- id 为 4 的记录行的记录锁。
- age 区间在(10,15)的间隙锁。
- age 区间在(15,20)的间隙锁。
最终,事务 A插入了该行数据,并锁住了(10,20)这个区间。
随后事务 B试图插入一行数据:
INSERT INTO users SELECT 5, "Louis", 16;
因为 16 位于(15,20)区间内,而该区间内又存在一把间隙锁,所以事务 B别说想申请自己的间隙锁了,它甚至不能获取该行的记录锁,自然只能乖乖的等待 事务 A结束,才能执行插入操作。
很明显,这样做事务之间将会频发陷入阻塞等待,插入的并发性非常之差。
这时如果我们再去回想我们刚刚讲过的插入意向锁,就不难发现它是如何优雅的解决了并发插入的问题。
总结
- InnoDB在RR的事务隔离级别下,使用插入意向锁来控制和解决并发插入。
- 插入意向锁是一种特殊的间隙锁。
- 插入意向锁在锁定区间相同但记录行本身不冲突的情况下互不排斥。
注:本文以 PDF 持续更新,最新尼恩 架构笔记、面试题 的PDF文件,请从下面的链接获取:语雀 或者 码云
参考文献:
分布式事务( 图解 + 秒懂 + 史上最全 ) - 疯狂创客圈 - 博客园 (cnblogs.com)https://www.cnblogs.com/crazymakercircle/p/13917517.html
https://cloud.tencent.com/developer/article/1478535
https://www.cnblogs.com/CnFallTime/p/16297502.html
https://www.cnblogs.com/zh770/p/15194297.html
https://blog.csdn.net/qq_38645103/article/details/125891108
https://blog.csdn.net/weixin_39559282/article/details/119210641
https://blog.csdn.net/qq_39487835/article/details/123787952
https://www.cnblogs.com/aspirant/p/6920987.html
https://blog.csdn.net/qq_44969643/article/details/125000906
https://blog.csdn.net/qq_27828675/article/details/115955740
https://mp.weixin.qq.com/s/IIyimk50T0mV4PJXjaM1wQ
https://zhuanlan.zhihu.com/p/436733017
推荐阅读:
《尼恩Java面试宝典》《Springcloud gateway 底层原理、核心实战 (史上最全)》《Flux、Mono、Reactor 实战(史上最全)》《sentinel (史上最全)》《Nacos (史上最全)》《分库分表 Sharding-JDBC 底层原理、核心实战(史上最全)》《TCP协议详解 (史上最全)》《clickhouse 超底层原理 + 高可用实操 (史上最全)》《nacos高可用(图解+秒懂+史上最全)》《队列之王: Disruptor 原理、架构、源码 一文穿透》《环形队列、 条带环形队列 Striped-RingBuffer (史上最全)》《一文搞定:SpringBoot、SLF4j、Log4j、Logback、Netty之间混乱关系(史上最全)《单例模式(史上最全)《红黑树( 图解 + 秒懂 + 史上最全)》《分布式事务 (秒懂)》《缓存之王:Caffeine 源码、架构、原理(史上最全,10W字 超级长文)》《缓存之王:Caffeine 的使用(史上最全)》《Java Agent 探针、字节码增强 ByteBuddy(史上最全)》《Docker原理(图解+秒懂+史上最全)》《Redis分布式锁(图解 - 秒懂 - 史上最全)》《Zookeeper 分布式锁 - 图解 - 秒懂》《Zookeeper Curator 事件监听 - 10分钟看懂》《Netty 粘包 拆包 | 史上最全解读》《Netty 100万级高并发服务器配置》《Springcloud 高并发 配置 (一文全懂)》
-
焦点热议:核心面试题:MVCC、间隙锁、Undo Log链、表级锁、行级锁、页级锁、共享锁、排它锁、记录锁等等
文章很长,而且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录博客园版为您奉上珍贵的学习资源:免...
来源: 焦点热议:核心面试题:MVCC、间隙锁、Undo Log链、表级锁、行级锁、页级锁、共享锁、排它锁、记录锁等等
微信iOS版拍照“大升级”:终于支持微距拍摄
当前快看:支付宝新增“极速模式”:自动收起首页推荐 更清爽了
当前关注:连花清瘟可致肝损伤肝衰竭?药企回应:严重误导
腾讯:2022年游戏盗号量上涨300% DDoS攻击全行业最高
花费13亿、飞了540万公里:韩国探测器终于进入月球轨道
北航计算机网络实验复习——设计性实验汇总
天天观察:6GHz就这?!Intel i9-13900KS跑分勉强提升5%
微速讯:油管上最爆火的恐怖游戏:被托马斯小火车追杀
电池供电不插线:世界首款真无线电视将在CES亮相
快看:3D领域大神约翰·卡马克宣布彻底离开Meta:称其效率低到无法忍受
男子按导航开车到冰冻江面 一头栽入松花江
当前最新:概念、场景技术方案选择的理解
hive配置Tez引擎,并安装Tez-ui
天天要闻:超级好看的 Edge 浏览器新标签页插件:好用、好看、免费浏览器必备
【世界热闻】matplotlib绘图详解
当前观察:下标模意义下的多项式乘法及其应用
【世界聚看点】go-micro v3 rpc服务一次改造经历
天天讯息:索尼要爆发了!明年有望推出新款PS5:独占大作护航
MyBatis核心配置文件详解
每日播报!疯狂裁员后 曝马斯克正为推特寻找新买家
环球最资讯丨“三连跌” 明晚油价迎年内“最后一调”:92号汽油或回归7元时代
世界头条:豆瓣8.3分!《阿凡达:水之道》票房破3亿:会是救市之作吗
全球热推荐:低温蓝色预警!这些地方最低温较历史同期低7℃以上:南方网友瑟瑟发抖
克罗地亚2-1战胜摩洛哥夺世界杯季军 37岁莫德里奇告别世界杯赛场
焦点关注:[PingCTF2022] guess what - S1gMa
环球滚动:女子开特斯拉被查酒驾 罚2000元记12分:本人称吃醉蟹 交警回应
销售开特斯拉撞了顾客的特斯拉 拒赔5万元:直言刹车了停不住
智慧树视频课件课程下载工具,如何在电脑端下载智慧树视频课件PDF,PPT到本地
史上口碑最好的小米旗舰!小米13京东好评率接近100%
VUE组件
【世界时快讯】安全多方计算:(2)隐私信息检索方案汇总分析
全球快资讯:基于海拉克斯打造 丰田推出首款纯电皮卡原型车 网友:丑到我眼睛了
天天快资讯丨卡神离开Meta 批老东家效率低下:GPU利用率5%简直是侮辱
全球热点评!小米MIX Fold 2新配色下周首销:5.4mm厚度已是行业极限 8999元
今热点:同价位无敌手!小米万兆路由器更新
全球观热点:第八天 循环的花里胡哨的用法
重学c#系列——linq(4) [三十]
【速看料】400行的象棋程序
AMD、NVIDIA齐发新品 显卡厂商的好日子来了:加速去库存
世界快报:男子1199元买长江存储2TB SSD吐槽是翻新 手指划痕:网友看完笑死
世界快消息!用户真实评价小米13:太惊艳了 这就是小米13带给我的感觉
精彩看点:北欧品质 口感清爽:OATLY燕麦奶4.1元/斤新低(商超29.8元)
【天天新视野】男子称买到过期3月沐浴露获赔5元:看不起谁呢
焦点信息:java基础面试题
【报资讯】Docker的资源控制管理
全球热资讯!BBA的禁脔 垂涎者不止蔚来
当前通讯!Docker网络模式
环球热点评!冬天空调取暖不热 可能是这些问题
环球观速讯丨小米13成了!线下门店卖断货 供不应求
热点!web项目的开发---第二天
世界今头条!SpringCloud-Ribbon学习笔记
世界速看:挑战行业多项不可能 一加11即将发布:最强性能旗舰
世界动态:python语法笔记
令人气愤!女子曝无良汽修店为赚钱高速路上撒钉子专扎车胎
天天视讯!刘作虎:OPPO一加正式开启双品牌战略 100亿投资扶植一加
环球观速讯丨义乌开始生产阿根廷夺冠球衣 还是三颗星 阿根廷辟谣:假的 侵权
世界快播:“顶级大众” 宾利Batur公布工艺细节:210克黄金3D打印
老人手指被卡:消防员用开塞露救了差点被剪的蓝宝石戒指
全球快资讯:一文了解 Dubbo 的代码架构
今日报丨关于整数二分的详解
滚动:预编译#error的使用
全球要闻:YoloV7 标签匹配机 loss 计算详解
安全多方计算(1):不经意传输协议
每日快讯!元旦假期首日火车票今开抢!迎出行小高峰:五大热门目的地出炉
观天下!Find N2系列发布背后:OPPO再次展示对产品精益求精态度
天天要闻:全国冻哭预警地图来了:周末20余省份或被冻哭 冷到破纪录
热议:售价超300万!乔布斯亲手编号Apple-1电脑落锤成交:46年后 开机画面眼前一亮
世界最大独立圆柱体水族馆爆裂:1500多条鱼全军覆没
世界杯决战前夕 法国又有两大主力倒下!5人感染神秘流感
【环球热闻】被加价千元:AMD喊话正加大RX 7900系生产!NV 4080笑而不语
世界简讯:VUE数据双向绑定
第一百一十四篇: JS数组Array(三)数组常用方法
世界上最大的鱼缸今日突然破裂:100万升水泄露 1500条鱼死亡
放假3天不调休!2023年元旦假期首日火车票开售 除夕票这一天就能买
天天报道:学谁不好学特斯拉!几十万的宝马车 容不下一个收音机
观天下!骗子诈骗1250万 买彩票中1450万:已退还给39名聋哑人
全球热讯:XSX性能比PS5强 但为何游戏性能总是输?
天天速看:Python函数/动态参数/关键字参数
天天热点!女子发烧敷20分钟面膜揭下变3D立体面具 医生提醒:影响退热 当心面瘫
方便面消费第一大国是我们:超6成人每周吃三次
世界速读:注解在Android中的使用场景
热推荐:锂金属电池爱长枝晶?韩国科学家找到破解之法
【当前独家】确认了!《阿凡达2:水之道》没有片尾彩蛋
天天观速讯丨1152分区+4K 144Hz 联合创新32寸miniLED显示器首发5399元
终于修好了!Win11新补丁解决22H2大文件复制缓慢Bug
首款第二代骁龙8游戏旗舰!红魔8 Pro来了
今日最新!王思聪投资百万成立新公司 经营范围包括动漫游戏开发
全球最资讯丨全自动门锁比半自动更人性化!但半自动更受青睐 真相终于揭开
世界简讯:Hessian2序列化支持这一点,让重构dubbo接口更容易了
【热闻】-真正的国产亲民MPV 新款传祺M6 PRO上市:11.98万起
看点-旗下新作首月收入超4.8亿!腾讯成为《妮姬》开发商Shift Up第二大股东
女子高烧39.8度喊妈 妈妈以为鸭子叫没搭理 网友:怎么又变异了
观察-《三体》动画明天开播第3集 官方公布史强、古筝行动档案
【天天聚看点】微信小程序报错“getLocation:fail the api need to be declared in the requiredPriva
【全球快播报】记录--三分钟打造自己专属的uni-app工具箱
视点!小米万兆路由器用上企业级处理器!卢伟冰:降维打击
【天天报资讯】LCD面板价格连连下跌!LG P7 LCD工厂停产
环球关注:中国高速看山东!山东高速施工用上北斗卫星:精度达到毫米级
世界要闻:三大板卡品牌之一的微星缺席RX 7900首发 原因揭晓:直接非公版