type
status
date
slug
summary
tags
category
icon
password
整理定义
临键锁(Next-key Lock)= Gap Lock + Record Lock
临键锁是索引记录上的记录锁和索引记录之前的间隙上的间隙锁的组合。
复述展开
InnoDB 以这样的方式执行行级锁定:当它搜索或扫描表索引时,它会在遇到的索引记录上设置共享锁(S锁)或排他锁(X锁)。 因此,行级锁实际上是索引记录锁。 索引记录上的临键锁也会影响该索引记录之前的“间隙”。 也就是说,临键锁是索引记录锁加上索引记录之前间隙上的间隙锁。 如果一个会话对索引中的记录 R 具有共享锁或独占锁,则另一个会话无法在索引顺序中紧邻 R 之前的间隙中插入新索引记录。
假设索引包含值 10、11、13 和 20。该索引可能的临键锁涵盖以下区间,其中圆括号表示排除区间端点,方括号表示包含端点:
对于最后一个间隔,临键锁锁定索引中最大值和“supremum”伪记录的值高于索引中的任何值。上确界不是一个真正的索引记录,因此,实际上,这个下一个键锁只锁定最大索引值后面的间隙。
InnoDB的默认事务隔离级别为 可重复读(Repeated Read)。在这种情况下,InnoDB使用临键锁进行搜索和索引扫描,这可以防止幻读。
也可以理解为一种特殊的间隙锁。通过临建锁可以解决幻读
的问题。 每个数据行上的非唯一索引列上都会存在一把临键锁,当某个事务持有该数据行的临键锁时,会锁住一段左开右闭区间的数据。需要强调的一点是,InnoDB 中行级锁是基于索引实现的,临键锁只与非唯一索引列有关,在唯一索引列(包括主键列)上不存在临键锁
。
ㅤ | 事务A | 事务B |
T1 | UPDATE table SET name = Vladimir WHERE age = 14; | ㅤ |
T2 | ㅤ | INSERT INTO table VALUES(100, 19, 'tianqi'); |
T3 | commit | ㅤ |
T3 | ㅤ | commit |
T2会因为T1对 age 为 19的列进行锁定后,将(14,20]的区间进行了锁定
很明显,事务 A 在对 age 为 24 的列进行 UPDATE 操作的同时,也获取了(14,20] 这个区间内的临键锁。
首先说明一下,这些加锁规则我没在别的地方看到过有类似的总结,以前我自己判断的时候都是想着代码里面的实现来脑补的。这次为了总结成不看代码的同学也能理解的规则,是我又重新刷了代码临时总结出来的。所以,这个规则有以下两条前提说明:
- MySQL后面的版本可能会改变加锁策略,所以这个规则只限于截止到现在的最新版本,即5.x系列<=5.7.24,8.0系列 <=8.0.13。
- 如果大家在验证中有发现bad case的话,请提出来,我会再补充进这篇文章,使得一起学习本专栏的所有同学都能受益。
因为间隙锁在可重复读隔离级别下才有效,所以本篇文章接下来的描述,若没有特殊说明,默认是可重复读隔离级别。我总结的加锁规则里面,包含了两个“原则”、两个“优化”和一个“bug”。
- 原则1:加锁的基本单位是next-key lock。希望你还记得,next-key lock是前开后闭区间。
- 原则2:查找过程中访问到的对象才会加锁。
- 优化1:索引上的等值查询,给唯一索引加锁的时候,next-key lock退化为行锁。
- 优化2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock退化为间隙锁。
- 一个bug:唯一索引上的范围查询会访问到不满足条件的第一个值为止。
理解体会
这里对 记录锁、间隙锁、临键锁 做一个总结
- InnoDB 中的行锁的实现依赖于索引,一旦某个加锁操作没有使用到索引,那么该锁就会退化为
表锁
。
- 记录锁存在于包括主键索引在内的唯一索引中,锁定单条索引记录。
- 临键锁存在于非唯一索引中,锁定开区间范围内的一段间隔,它是基于间隙锁实现的。
快速跳转链接
【概念解析】启动
【概念解析】Day 1 - 10
【概念解析】Day 11 - 20
【概念解析】Day 21 - 30
【概念解析】Day 31 - 40
【概念解析】Day 41 - 50
【概念解析】Day 51 - 60
【概念解析】Day 61 - 70
【概念解析】Day 71 - 80
【概念解析】Day 81 - 90
- 作者:eachenkuang
- 链接:https://kuangyichen.com/article/industry-day79
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。