MySQL里的间隙锁以及加锁规则
Posted dejavuyj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL里的间隙锁以及加锁规则相关的知识,希望对你有一定的参考价值。
上一篇文章安装锁的粒度介绍了mysql里的锁.其中提到了间隙锁.
dejavuyj,公众号:dejavuyj
这篇文章专门介绍间隙锁.
一般情况下,只有在可重复读隔离级别下,才会用到间隙锁.因为间隙锁的主要作用是解决可重复读隔离级别下的幻读问题.
什么是幻读?
举个例子,表t有主键id,和字段a,表里有(3,3)和(6,6)两行数据.
session A执行了两次查询(使用的都是for update当前读),Q2读到(5,6)这一行的现象,就叫幻读.
幻读是在可重复读隔离级别下,同一个事务在两次查询同一个范围的时候,后一次查询查到了前一次查询没有看到的行.
这个概念有点复杂,出现幻读有两个前提:
首先是要在可重复读隔离级别下,如果是读提交隔离级别,是不存在幻读的问题的,因为读提交隔离级别下,后一次查询是可以查到其他事务提交的变更的
其次是要使用当前读,才会出现幻读. 在可重复读隔离级别下,普通查询是快照读,是看不到其他事务提交的数据的
另外需要注意的是,幻读专指"新插入的行",其他事务更新原有记录导致后续查询出之前没查出的记录,并不能叫幻读.
幻读是怎么产生的?
假设可重复读隔离级别下,一个事务的当前读只给已存在的行记录加锁,其他事务还是可以在两行的间隙之间插入新的数据.就像上面举的例子,(3,3),(6,6)的间隙里插入了(5,5)的记录.
InnoDB为了解决幻读,就引入了间隙锁(Gap Lock),间隙锁锁的是两个值之间的空隙.
间隙锁的范围是开区间,不包含两个端点.
间隙锁加行锁合起来就是next-key lock,next-key lock是前开后闭区间.
值得一提的是,间隙锁虽然解决了幻读问题,但是也可能会造成死锁.如果两个事务都加了间隙锁,然后分别执行insert语句就会产生死锁.
另外间隙锁会让同样的语句锁住更大的范围,这会降低系统的并发度.
根据林晓斌的总结,MySQL的加锁规则如下:
原则1: 加锁的基本单位是next-key lock,next-key lock是前开后闭区间
原则2: 查找过程中访问到的对象才会加锁
优化1: 索引上的等值查询, 给唯一索引加锁的时候, next-key lock退化为行锁
优化2: 索引上的等值查询, 向右遍历时且最后一个值不满足等值条件的时候, next-key lock退化为间隙锁
一个bug: 唯一索引上的范围查询会访问到不满足条件的第一个值为止(8.0.18 已修复)
间隙锁和覆盖索引的问题:
lock in share mode只锁覆盖索引, 没有访问主键索引, 所以主键索引上不会加锁
for update 会给主键索引加锁
limit加锁
使用limit可以减小加锁的范围,从而提高并发度
参考文献
极客时间专栏 - MySQL实战45讲 林晓斌
以上是关于MySQL里的间隙锁以及加锁规则的主要内容,如果未能解决你的问题,请参考以下文章
Day885.NextKeyLock加锁规则 -MySQL实战