什么是间隙锁

间隙锁(Gap Lock):间隙锁是(RR级别下)一个在索引记录之间的间隙上的锁,可以是两个索引记录之间,也可能是第一个索引记录之前或最后一个索引之后的空间
首先要明确,间隙锁是为了防止幻读而产生的锁。在RR下才生效,在RC下锁算法就是记录锁,而在RR情况间隙锁会生效,虽然RR也不能完全避免幻读,因为直接select并不上锁
间隙锁的作用:
间隙锁是MySQL行锁的一种,与行锁不同的是间隙锁可能锁定的是一行数据,也可能锁住一个间隙。锁定规则如下:
当修改的数据存在时,间隙锁只会锁定当前行。
当修改的数据不存在时,间隙锁会向左找第一个比当前索引值小的值,向右找第一个比当前索引值大 的值(没有则为正无穷),将此区间锁住,从而阻止其他事务在此区间插入数据。
比如事务A update table ···· where id >100
那么加上这个锁,不仅id大于100的行记录上锁,还能保证id>100区间在 A未提交之前不能插入新的数据。
间隙锁是封锁索引记录中的间隔,或者第一条索引记录之前的范围,又或者最后一条索引记录之后的范围。
产生间隙锁的条件(RR事务隔离级别下;):
使用普通索引锁定;
使用多列唯一索引;
使用唯一索引锁定多行记录。
以上情况,都会产生间隙锁,
当我们用范围条件而不是相等条件索引数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项枷锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”。
InnoDB也会对这个“间隙”枷锁,这种锁机制就是所谓的间隙锁(Next-Key锁)。

临键锁

临键锁,是记录锁与间隙锁的组合,它的封锁范围,既包含索引记录,又包含索引区间。
注:临键锁的主要目的,也是为了避免幻读(Phantom Read)。如果把事务的隔离级别降级为RC,临键锁则也会失效。

插入意向锁

插入意向锁是对插入操作的优化,仅仅插入数据时不会产生对某个区间的间隙锁,而是检测区间是否正在被间隙锁锁定,如果是会被阻塞,否则进行插入。是对间隙锁在插入数据这个场景的优化
(1)插入意向锁是一种Gap锁,不是意向锁,在insert操作时产生。
(2)在多事务同时写入不同数据至同一索引间隙的时候,并不需要等待其他事务完成,不会发生锁等待。
(3)假设有一个记录索引包含键值4和7,,在没用间隙锁,行锁,等其他锁的情况下,不同的事务分别插入5和 6,每个事务都会产生一个加在4-7之间的插入意向锁,获取在插入行上的排它锁,但是不会被互相锁住,因为数据行并不冲突。
(4)插入意向锁不会阻止任何锁,但是会被其他锁阻止,例如间隙锁,对于插入的记录会持有一个记录锁。
插入意向锁实际上就是一种间隙锁,不过这种间隙锁不会阻塞其他锁,是对间隙锁针对插入的优化。

间隙锁的危害

因为Query执行过程中通过范围查找的话,他会锁定整个范围内所有的索引键值,即使这个键值并不存在。间隙锁有一个比较致命的弱点,就是当锁定一个范围键值之后,即使某些不存在的键值也会被无辜的锁定,也造成在锁定的时候无法插入锁定键值范围内的任何数据。在某些场景下这可能会对性能造成很大的危害。

间隙锁与导致超时

最近用户反馈说系统老是出现insert时,等待超时了,最后发现是insert间隙锁!间隙锁是innodb中行锁的一种, 但是这种锁锁住的却不止一行数据,他锁住的是多行,是一个数据范围。间隙锁的主要作用是为了防止出现幻读,但是它会把锁定范围扩大,
有时候也会给我们带来麻烦,我们就遇到了。 在数据库参数中, 控制间隙锁的参数是:
innodb_locks_unsafe_for_binlog
这个参数默认值是OFF, 也就是启用间隙锁, 他是一个bool值, 当值为true时表示disable间隙锁。
那为了防止间隙锁是不是直接将innodb_locaks_unsafe_for_binlog设置为true就可以了呢? 不一定!
而且这个参数会影响到主从复制及灾难恢复, 这个方法还尚待商量。

间隙锁导致死锁

情况一(两个事务互相锁住对方区间)

我们先准备一个表mysql> select * from t_gap_lock;
±—±-------±-----+
| id | name | age |
±—±-------±-----+
| 1 | 张一 | 21 |
| 5 | 李五 | 25 |
| 6 | 赵六 | 26 |
| 9 | 王九 | 29 |
| 12 | 十二 | 12 |
±—±-------±-----+
表中的id数据咱们准备了三个间隙:

  • 间隙一:1-5
  • 间隙二:6-9
  • 间隙三:12-正无穷

1、此时我们开启事务一,然后执行更新id=3的数据,按照咱们的理论,id=3这个数据不存在,说明它会在1-5之间加间隙锁。
2、然后我们开启事务二,然后执行更新id=7的数据,按照咱们的理论,id=7这个数据不存在,说明它
那么重点来了,此时我们需要做的操作就是让事务一在6-9之间插入数据,会发现此时事务已经被阻塞,无法执行insert,因为事务二已经对该区间加了间隙锁
在事务一等待锁的同时,咱们让事务二同时在1-5之间插入数据,这个时候会发现,只要事务二一执行插入。MySQL立即报了死锁,我们就会见到如下提示:
[40001][1213] Deadlock found when trying to get lock; try restarting transaction 。
整个死锁过程进行原理分析
1、首先事务一开启事务后,更新id=3的数据,此数据不存在,所以事务一会锁住1-5这个间隙,即为1-5这个间隙添加间隙锁,同理,事务二会为6-9这个间隙添加间隙锁;
2、然后我们让事务一在6-9这个间隙插入数据,因为事务二已经加了间隙锁,所以事务一需要等待事务二释放间 隙锁才能进行插入操作,此时事务一等待事务二释放间隙锁;
3、同理,事务二在1-5间隙插入时需要等待事务一释放间隙锁,两个事务相互等待,死锁产生。
那么咱们此时就能大概明白最初那个Mybatis-plus的saveOrUpdate方法为什么会造成间隙锁死锁的问题,也就是线上存在两个并发事务,然后更新的时候都没有更新到,此时都在自己的间隙加了间隙锁,然后再到彼此的区间进行数据插入,此时就会造成两个事务互相等待对方的释放间隙锁,从而导致死锁。也许有同学会想,线上的数据几乎不可能刚好会存在1-5,6-9这种间隙,来给并发事务各自加锁,又刚好到彼此区间插入数据的场景,所以我们就会有接下来验证间隙锁加锁是非互斥的,再一次深度还原间隙锁死锁的场景。

情况二(间隙锁非互斥)

验证间隙锁加锁非互斥
然以t_gap_lock为例

mysql> select * from t_gap_lock;
±—±-------±-----+
| id | name | age |
±—±-------±-----+
| 1 | 张一 | 21 |
| 5 | 李五 | 25 |
| 6 | 赵六 | 26 |
| 9 | 王九 | 29 |
| 12 | 十二 | 12 |
±—±-------±-----+
1、此时咱们开启事务一,然后执行更新id=13的数据,按照咱们的理论,id=13这个数据不存在,说明它会在13-正无穷(因为当前索引树上没有比13更大的值)之间加间隙锁。
2、然后我们开启事务二,然后也执行更新id=13的数据,按照咱们的理论,事务二也会对13-正无穷之间加间隙锁
3、那么重点来了,此时我们需要做的操作就是让事务一在13-正无穷之间插入数据,会发现此时事务已经被阻塞,无法执行insert,因为事务二已经对该区间加了间隙锁。
4、在事务一等待锁的同时,咱们让事务二同时在13-正无穷之间插入数据,这个时候会发现,只要事务二一执行插入。MySQL立即报了死锁,我们就会见到如下提示:
[40001][1213] Deadlock found when trying to get lock; try restarting transaction 。
因为咱们已经用1-5以及6-9这种明显的间隙还原了间隙锁死锁,所以13-正无穷发生间隙锁死锁的原理与其无异,这里有个非常大的区别就是事务一已经在13-正无穷加了间隙锁,事务二依然可以对此间隙加间隙锁,所以我们用实际证明了间隙锁加锁是非互斥的。此时咱们回忆一下Mybatis-plus的saveOrUpdate方法,发现线上只要出现两个并发事务去修改同一条不存在的数据,就会立马出现间隙锁死锁。

情况三(当修改数据存在时,间隙锁只会锁住当前行)

select * from t_gap_lock;
±—±-------±-----+
| id | name | age |
±—±-------±-----+
| 1 | 张一 | 21 |
| 5 | 李五 | 25 |
| 6 | 赵六 | 26 |
| 9 | 王九 | 29 |
| 12 | 十二 | 12 |
±—±-------±-----+
1、此时我们开启事务一,然后执行更新id=12的数据,按照咱们的理论,id=12这个数据存在,说明MySQL只会锁定id=12这一行数据。
2、然后我们开启事务二,然后执行更新id=13的数据,按照咱们的理论,id=13这个数据不存在,说明它会在13-正无穷(因为当前索引树上没有比13更大的值)之间加间隙锁
3、那么重点来了,此时我们需要做的操作就是让事务一在13-正无穷之间插入数据,会发现此时事务已经被阻塞,无法执行insert,因为事务二已经对该区间加了间隙锁。
4、在事务一等待锁的同时,咱们让事务二在12-正无穷之间插入数据,这个时候会发现,事务二能够正常插入,说明事务二没有被间隙锁阻塞,待事务二提交或回滚后,事务一也正常提交。
5、通过以上验证,MySQL在更新id=12,即数据存在时,并没有对12-正无穷添加间隙锁,而是只锁定了4、id=12这一行数据,从而降低锁的颗粒度以提高性能。

间隙锁能够防止幻读吗?

那么我们说RR级别下的幻读是怎么产生的呢
Mysql官方给出的幻读解释是:只要在一个事务中,第二次select多出了row就算幻读。
a事务先select,b事务insert确实会加一个gap锁,但是如果b事务commit,这个gap锁就会释放(释放后a事务可以随意dml操作),a事务再select出来的结果在MVCC下还和第一次select一样,接着a事务不加条件地update,这个update会作用在所有行上(包括b事务新加的),a事务再次select就会出现b事务中的新行,并且这个新行已经被update修改了,实测在RR级别下确实如此。
可见如果在RR隔离级别,快照读情况下,间隙锁与MVCC同时发挥作用的情况下,是无法完全防止幻读的
如果select for update时上锁,那么就会自然加上gap锁,那么就可以避免幻读
针对当前读(select … for update 等语句),是通过 next-key lock(记录锁+间隙锁方式解决了幻读),因为当执行 select … for update 语句的时候,会加上 next-key lock,如果有其他事务在 next-key lock 锁范围内插入了一条记录,那么这个插入语句就会被阻塞,无法成功插入,所以就很好了避免幻读问题。

MySQL间隙锁详细分析相关推荐

  1. 八、Mysql 间隙锁(gap 锁)与慢查询

    Mysql 间隙锁(gap 锁)与慢查询 gap锁与慢查询 gap锁 事务语法 开启事务 事务回滚 事务提交 还原点(演示) 业务设计 逻辑设计 范式设计 查询测试 反范式设计 总结 范式化设计优缺点 ...

  2. TiDB和MySQL的锁一些分析比对

    原文来源: https://tidb.net/blog/1c1a2ab7 [是否原创]是 [首发渠道]TiDB 社区 [首发渠道链接]其他平台首发请附上对应链接 [正文] 图1 锁分类图 一.悲观锁和 ...

  3. MySQL间隙锁(幻读解决原理)

    文章目录 一.间隙锁概念 二.测试间隙锁范围加锁 场景1:用不可重复的主键id测试间隙锁 场景2:用可重复的age(有索引)测试间隙锁 场景3:实际情况需要具体分析用的到底是行锁还是表锁 三.测试等值 ...

  4. mysql间隙锁 打开_MySQL间隙锁问题

    间隙锁(Gap Lock):锁加在不存在的空闲空间,可以是两个索引记录之间,也可能是第一个索引记录之前或最后一个索引之后的空间. 最近用户反馈说系统老是出现insert时,等待超时了,最后发现是ins ...

  5. mysql间隙锁可重入_关于mysql 间隙锁

    前段时间系统老是出现update死锁,很是纠结.经过排查发现是间隙锁!间隙锁是innodb中行锁的一种, 但是这种锁锁住的却不止一行数据,他锁住的是多行,是一个数据范围.间隙锁的主要作用是为了防止出现 ...

  6. Mysql间隙锁实战

    前言 本次实战是基于数据库mysql5.7.14 什么是间隙锁? 间隙锁是对索引记录之间间隙的锁,或者对第一个索引记录之前或最后一个索引记录之后的间隙的锁.例如:SELECT c1 FROM t WH ...

  7. mysql间隙锁触发条件,详解系列文章

    java基础 1.1java的8种基本数据类型装箱拆箱 1.2重写重载封装继承多态 1.3 Stack Queue 1.7 Concurrent包 1.8面向对象 1.9 String StringB ...

  8. MySQL索引的详细分析和数据结构

    1.背景描述 索引是帮助MySQL高效查找的数据结构,他的本质是空间换时间.查找可以分为两种,第一种顺序查找,mysql中最差的情况下就是全表扫描,一行一行数据找直到找到全部符合条件的项目,显然在大量 ...

  9. 2021字节跳动春招技术面试题:mysql间隙锁触发条件

    java基础 1.1java的8种基本数据类型装箱拆箱 1.2重写重载封装继承多态 1.3 Stack Queue 1.7 Concurrent包 1.8面向对象 1.9 String StringB ...

最新文章

  1. Linux rm过滤后的目录6,Linux的部分命令
  2. Ubuntu Vmwaretools安装说明
  3. 自控matlab设计,自动控制原理课程设计--基于MATLAB软件的自动控制系统仿真
  4. GIT学习笔记2--基本使用
  5. 30 行 Javascript 代码搞定智能家居系统
  6. 一文详解 Try 和异常的区别
  7. 使用wepy 小程序授权点击取消授权失败的方案
  8. 计算机游戏制作的要求,游戏设计美工需要什么样的电脑配置
  9. 入门系列之基于MATLAB的滚动轴承内外圈复合线性剥落故障动力学建模
  10. 北斗卫星导航系统基础篇之(二)
  11. 针对英特尔xtu超频软件安装失败以及英伟达GeForce Experience安装程序无法继续的解决方法
  12. 使用python开发的GUI可视化界面植物名录查询系统,使用python读取xls文件,读取xlsx文件。tkinter使用
  13. LoadRunner11代理在Win10操作系统启动不起来,或者报错:该内存不能为written
  14. CAD2018下载AutoCAD2018下载安装教程附软件下载
  15. 如何注册域名的详细图文过程分享
  16. 为什么我们要学习Microsoft Graph
  17. 墨菲定律 三种(is2120)
  18. android 提高启动速度慢,安卓启动速度过慢的原因及解决方法
  19. 使用vba操作工作表,实现报表汇总
  20. 他山之石 | 丁香园 医疗领域图谱的构建与应用

热门文章

  1. 质性分析软件nvivo的学习(三)
  2. 线性系统的校正之串联校正
  3. 多个电子面单接口平台分析和对接
  4. win7关闭休眠_你的Windows7系统运行缓慢?给你一个Win7系统减肥攻略
  5. PMON学习记录2:PMON启动流程1
  6. Java-实现动态数组(ArrayList<Integer>集合)
  7. 推荐项目_动手学深度学习pytorch版
  8. 考研数学高数1-1综合测试-函数及其性质
  9. 白话空间统计二十九:空间插值(二)
  10. 计算机组装与维护双系统安装,怎么装双系统win7和win10?怎么装双系统教程