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 将对表中所有数据加锁,实际和表记锁一样。

加锁的规则总结以下几点:

  1. 加锁的基本单位是next-key lock 左开右闭
  2. 查询过程中只要访问到的数据都会加锁
  3. 唯一索引等值查询时,需要访问到第一个不满足条件的值,如果匹配next-key lock 会退化为行锁。
  4. 索引等值查询时,需要访问到第一个不满足条件的值,此时的next-key lock会退化为间隙锁(GAP lock)
  5. 索引范围查询需要访问到不满足条件的第一个值为止

实例:
创建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 加锁规则相关推荐

  1. 《MySQL——加锁规则(待补全,有些没看懂)》

    catalog 加锁规则 等值查询间隙锁 非唯一索引等值锁 主键索引范围锁 非唯一索引范围锁 唯一索引范围锁 bug 非唯一索引上存在"等值"的例子 limit语句加锁 关于死锁 ...

  2. mysql limit锁_我所理解的MySQL五:锁及加锁规则

    mysql教程栏目介绍MySQL的第五篇文章,关于锁及加锁规则. MySQL 系列的第五篇,主要内容是锁(Lock),包括锁的粒度分类.行锁.间隙锁以及加锁规则等. MySQL 引入锁的目的是为了解决 ...

  3. 聊聊MySQL的加锁规则《死磕MySQL系列 十五》

    大家好,我是咔咔 不期速成,日拱一卒 本期来聊聊MySQL的加锁规则,知道这些规则后可以判断SQL语句的加锁范围,同时也可以写出更好的SQL语句,防止幻读问题的产生,在能力范围内最大程度的提升MySQ ...

  4. 《MySql学习》 MySQL的 加锁规则

    MySQL加锁原则 两个原则 原则 1:加锁的基本单位是 next-key lock.next-key lock 是前开后闭区间(区间锁和行锁). 原则 2:查找过程中访问到的对象(索引)才会加锁. ...

  5. 就这一次,带你彻底搞清 MySQL行级锁的加锁规则

    大家好,我是小林. 是不是很多人都对 MySQL 加行级锁的规则搞的迷迷糊糊,一会是 next-key 锁,一会是间隙锁,一会又是记录锁. 坦白说,确实还挺复杂的,但是好在我找点了点规律,也知道如何如 ...

  6. postgresql select for update 多行加锁顺序_insert into select加锁规则补充

    insert into select加锁规则补充 昨天的文章中,针对insert into select语句的加锁情况进行了分析: insert into A select * from B; 形如这 ...

  7. 【数据库】MySQL 加锁处理分析

    一.背景 MySQL/InnoDB的加锁分析,一直是一个比较困难的话题.我在工作过程中,经常会有同事咨询这方面的问题.同时,微博上也经常会收到MySQL锁相关的私信,让我帮助解决一些死锁的问题.本文, ...

  8. MySQL-间隙锁-加锁规则

    MySQL 后面的版本可能会改变加锁策略,本文只适用版本: 5.x 系列 <=5.7.24 8.0 系列 <=8.0.13 间隙锁在可重复读隔离级别下才有效,以下规则只描述了间隙锁: 原则 ...

  9. mysql加锁分析 何登成_何登成的技术博客 ? MySQL 加锁处理分析

    背景 MySQL/InnoDB的加锁分析,一直是一个比较困难的话题.我在工作过程中,经常会有同事咨询这方面的问题.同时,微博上也经常会收到MySQL锁相关的私信,让我帮助解决一些死锁的问题.本文,准备 ...

最新文章

  1. java分享第五天(数组)
  2. LeetCode Remove Element
  3. golang中的strings.EqualFold
  4. 我去,为什么最左前缀原则失效了?
  5. 【Linux】一步一步学Linux——hexdump命令(267)
  6. python 通过pip安装库 pycharm里面使用第三方库
  7. 20135316王剑桥Linux内核学习笔记第三周
  8. STM32——库函数版——矩阵按键程序
  9. word转换成pdf转换器2015绿色版
  10. java 最大流最小割_最大流, 最小割问题及算法实现
  11. hadoop-uber作业模式
  12. mybatis系列-03-入门程序
  13. 嵌入式电子钢琴游戏开发设计
  14. 编译原理(八)消除空产生式
  15. 14个种类,600款笔刷!如何做一套属于自己的精美笔刷?
  16. Java初级·基础语法
  17. 赶紧入手,python面试题之Python如何实现单例模式?
  18. 计算机老师副业能做什么,教师除了本职工作,还能做哪些副业?
  19. Android事件分发机制及设计思路,先收藏了
  20. 零基础微信完整版小程序开发之微信表情包小程序前端+后台源码(java)

热门文章

  1. c语言星座图原理,通信原理中星座图详解
  2. 基于django的个人博客开发
  3. 国考证监会计算机考试真题
  4. ps抠图——抠出自己想要的素材(钢笔工具的使用)
  5. 两个顺序栈共享一个数组的存储空间
  6. 整理控|四象限系列电脑桌面壁纸分享一波
  7. jsp基于java廉价房屋租赁管理系统
  8. outlook邮箱链接如何用ie打开_Outlook手机邮箱配置文档
  9. jmeter json取样器实现接口之间的关联
  10. K-近邻算法预测电影类型