MySQL 加锁规则
1、名词解释
1.1、快照读
快照读简单的来说就是简单的select 操作,没有加任何锁。在Innodb 存储引擎下执行简单的select操作时,会记录当前的快照读数据,之后的select 会沿用第一次快照读的数据,即使有其他事务提交也不会影响当前select 结果,因此通过快照读查询的数据虽然是一致的,但有可能不是最新的数据,而是历史数据。
1.2、当前读
使用select … for update 或者 update、delete 读取到的都是当前最新的数据。(update\delete 去修改数据的时候都是先查询出最新的数据)
2、next-key lock 加锁规则
前提:存储引擎支持行锁。
细节:
在MySQL 中,行级锁并不是直接锁记录,而是锁索引。InnoDB 行锁是通过给索引加锁实现的。
索引分为主键索引和非主键索引。
如果一条SQL 语句操作了主键索引,MySQL 就会锁定这条主键索引;
如果一条语句操作了非主键索引,MySQL 会先锁定该非主键索引,再锁定相关的主键索引。如果没有索引,InnoDB 会通过隐藏的聚簇索引来记录加锁。也就是说:如果不通过索引条件检索数据,那么InnoDB 将对表中所有数据加锁,实际和表记锁一样。
加锁的规则总结以下几点:
- 加锁的基本单位是next-key lock 左开右闭
- 查询过程中只要访问到的数据都会加锁
- 唯一索引等值查询时,需要访问到第一个不满足条件的值,如果匹配next-key lock 会退化为行锁。
- 索引等值查询时,需要访问到第一个不满足条件的值,此时的next-key lock会退化为间隙锁(GAP lock)
- 索引范围查询需要访问到不满足条件的第一个值为止
实例:
创建user 表,建表的初始化语句如下:
CREATE TABLE `user`
( `id` int(11) NOT NULL AUTO_INCREMENT,`class` tinyint(4) NOT NULL,`name` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE,KEY `idx_class` (`class`) USING BTREE)
ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
插入数据:
INSERT INTO next_key_lock (`class`,`name`)
VALUES
(1,'咔咔'),
(3,'小刘'),
(8,'小张'),
(15,'小李'),
(20,'张但'),
(25,'王五'),
(25,'李四');
2.1、唯一索引等值查询
修改id 为 9 的记录
新增一条记录
修改id 为 7的记录
分析这条SQL 满足的规则:
规则一:查询过程中只要访问到的数据都会加锁,加锁的基本单位是next-key lock,左开右闭。
规则二:唯一索引等值查询,next-key lock 退化为行锁。
规则三:索引等值查询,需要访问到第一个不满足条件的值,此时的next-key lock 会退化为间隙锁。
根据规则一:加锁范围为(7,∞]
根据规则二:退化为行锁,但明显此条SQL 不满足条件,因为表里不存在id=9的这条记录,所以此规则不生效。
根据规则三:next-key lock 退化为间隙锁,加锁范围为(7,∞)
结论:
唯一索引等值查询时,行数据存在的时候是行锁,行数据不存在,那就是好间隙锁。
因此终端2的语句会一直处于等待状态,直到终端1执行完成。
2.2、普通索引等值查询
给class=8 这条记录加共享读锁
修改id = 3 这条记录的 name 值
新增一条记录 class = 9
分析这条SQL 满足哪些规则:
规则一:查询过程中只要访问到的数据都会加锁,加锁的基本单位next-key lock,左开右闭状态。
规则二:索引等值查询,需要访问到第一个不满足条件的值,此时的next-key lock 会退化伟间隙锁。
根据规则一:加锁范围是(3,8]
根据规则二:需要访问到第一个不满足的值,加锁的范围(8,15]
,又因为会退化为间隙锁,加锁范围变为(8,15)
结论:
三条SQL 执行后,第二个SQL执行成功,第三个SQL等待。
第三个SQL新增的值是9,在锁范围内需要等第一个SQL提交事务后才执行成功。
为什么第二个SQL会执行成功???
总结的加锁规则中,查询过程中访问到的数据都会加锁,但第二个SQL使用的是覆盖索引,所以并不需要回表查询主键索引,所以主键索引上是没有加任何锁的。
在B+tree中主键索引叶子节点存储的是整行数据,而普通索引叶子节点存储的是主键的值。
扩展
当前这个例子中,加的是lock in share mode 共享锁,锁的是覆盖索引,但是如果是for update就会给主键索引上满足条件的行加上行锁。所以使用覆盖所有是避免不了数据被热更新的,若要实现数据避免更新就需要绕过覆盖索引的优化。
使用for update 会给主键索引加锁,如果查询条件为普通索引但值存在多个相同的数据的,此时的加锁就会根据主键索引加锁。
2.3、主键索引范围锁
当前表记录:
给id >= 8 and id < 10 加范围锁
新增一条记录 class = 30,name = 李四1111111111111
修改id = 8 的记录的name值
从上面的案例得知:
第二个SQL和第三个SQL都处于等待第一个SQL执行中
分析上面案例的加锁规则:
规则一:访问到的数据都会加锁
规则二:唯一索引等值查询,next-key lock 会退化为行锁
根据规则一:加锁范围(7,8]
根据规则二:退化为行锁,加锁范围只是id=8 这一行
根据规则三:范围查询就往后找到第一个不满足条件的值,这里id=8后面没有记录,所以加锁的范围是(8,∞]
结论:
此条SQL加锁范围,行锁id=8,next-key lock (8,∞]
为什么从next-key lock 退化为行锁???
首先需要明白是等值判断还是范围判断,指这一行数据被查询选中的时候走的判断条件是通过a=b还是a>b 或a<b 来确定的。直白点就是这行数据是通过等值来的还是范围查询来的。
从SQL执行结果可得知数据是根据id = 8来的,因此next-key lock 会退化为行锁。
2.4、普通索引范围锁
执行SQL为:
select * from user wehre class >= 8 and class < 10 for update;
可以看到这个SQL 和前面那个主键索引范围锁非常相似,唯一区别就是普通索引没有退化行锁的规则。
分析这条SQL 满足哪些规则:
规则一:索引等值查询需要访问到第一个不满足的值,next-key lock 退化为间隙锁
规则二:索引范围查询需要访问到不满足条件的第一个值为止
根据规则一:加锁范围(7,8]
根据规则二:加锁范围(8,15]
结论:
加锁的范围为(7,8]
,(8,15]
为什么没有退化为间隙锁???
仔细看规则得出,索引等值查询需要访问到不满足的值才会退化为间隙锁,此时是可以访问到8这个数据的,因此不会退化为间隙锁。
2.5、普通索引倒叙范围锁
在以上的所有案例中都是默认正序规则,接下来看倒叙的加锁规则是怎么样的。
执行SQL 为:
select * from next_key_lock where class >= 15 and class<=20 order by desc lock in share mode;
由于SQL加上了order by,因此第一个要定位class 索引最右边的值,也就是class = 20,因为class 是普通索引等值查询,因此会加上next-key lock 左开右闭 (15,20]
,普通索引等值查询会访问到不满足条件的值为止,所以还会继续扫描,直到遇到25,又会加上一个next-key lock (20,25]
,又因为25不满足查询条件,因此会退化为间隙锁(20,25)
。
还有一个条件是class >= 15,向左扫描到class = 8 才会停下来知道了是小于15 了,加锁单位是next-key lock,左开右闭范围是(3,8]
。
又因为查询是 * ,绕过了覆盖索引,需要回表查询,因此给主键ID也会加锁,加锁为id = 4,id = 5 两个行锁。
结论:
这条SQL 加锁范围是在索引class 是(3,25),主键索引上id = 4, 5 两个行锁。
3、总结
唯一索引等值查询,如果查询到数据,next-key lock 会退化为行锁,如果查询不到数据则依赖是间隙锁。
普通索引等值查询,next-key lock 退化为间隙锁。
MySQL 加锁规则相关推荐
- 《MySQL——加锁规则(待补全,有些没看懂)》
catalog 加锁规则 等值查询间隙锁 非唯一索引等值锁 主键索引范围锁 非唯一索引范围锁 唯一索引范围锁 bug 非唯一索引上存在"等值"的例子 limit语句加锁 关于死锁 ...
- mysql limit锁_我所理解的MySQL五:锁及加锁规则
mysql教程栏目介绍MySQL的第五篇文章,关于锁及加锁规则. MySQL 系列的第五篇,主要内容是锁(Lock),包括锁的粒度分类.行锁.间隙锁以及加锁规则等. MySQL 引入锁的目的是为了解决 ...
- 聊聊MySQL的加锁规则《死磕MySQL系列 十五》
大家好,我是咔咔 不期速成,日拱一卒 本期来聊聊MySQL的加锁规则,知道这些规则后可以判断SQL语句的加锁范围,同时也可以写出更好的SQL语句,防止幻读问题的产生,在能力范围内最大程度的提升MySQ ...
- 《MySql学习》 MySQL的 加锁规则
MySQL加锁原则 两个原则 原则 1:加锁的基本单位是 next-key lock.next-key lock 是前开后闭区间(区间锁和行锁). 原则 2:查找过程中访问到的对象(索引)才会加锁. ...
- 就这一次,带你彻底搞清 MySQL行级锁的加锁规则
大家好,我是小林. 是不是很多人都对 MySQL 加行级锁的规则搞的迷迷糊糊,一会是 next-key 锁,一会是间隙锁,一会又是记录锁. 坦白说,确实还挺复杂的,但是好在我找点了点规律,也知道如何如 ...
- postgresql select for update 多行加锁顺序_insert into select加锁规则补充
insert into select加锁规则补充 昨天的文章中,针对insert into select语句的加锁情况进行了分析: insert into A select * from B; 形如这 ...
- 【数据库】MySQL 加锁处理分析
一.背景 MySQL/InnoDB的加锁分析,一直是一个比较困难的话题.我在工作过程中,经常会有同事咨询这方面的问题.同时,微博上也经常会收到MySQL锁相关的私信,让我帮助解决一些死锁的问题.本文, ...
- MySQL-间隙锁-加锁规则
MySQL 后面的版本可能会改变加锁策略,本文只适用版本: 5.x 系列 <=5.7.24 8.0 系列 <=8.0.13 间隙锁在可重复读隔离级别下才有效,以下规则只描述了间隙锁: 原则 ...
- mysql加锁分析 何登成_何登成的技术博客 ? MySQL 加锁处理分析
背景 MySQL/InnoDB的加锁分析,一直是一个比较困难的话题.我在工作过程中,经常会有同事咨询这方面的问题.同时,微博上也经常会收到MySQL锁相关的私信,让我帮助解决一些死锁的问题.本文,准备 ...
最新文章
- java分享第五天(数组)
- LeetCode Remove Element
- golang中的strings.EqualFold
- 我去,为什么最左前缀原则失效了?
- 【Linux】一步一步学Linux——hexdump命令(267)
- python 通过pip安装库 pycharm里面使用第三方库
- 20135316王剑桥Linux内核学习笔记第三周
- STM32——库函数版——矩阵按键程序
- word转换成pdf转换器2015绿色版
- java 最大流最小割_最大流, 最小割问题及算法实现
- hadoop-uber作业模式
- mybatis系列-03-入门程序
- 嵌入式电子钢琴游戏开发设计
- 编译原理(八)消除空产生式
- 14个种类,600款笔刷!如何做一套属于自己的精美笔刷?
- Java初级·基础语法
- 赶紧入手,python面试题之Python如何实现单例模式?
- 计算机老师副业能做什么,教师除了本职工作,还能做哪些副业?
- Android事件分发机制及设计思路,先收藏了
- 零基础微信完整版小程序开发之微信表情包小程序前端+后台源码(java)