写点什么

10 个人 9 个答错,另外 1 个只对一半:数据库的锁

用户头像
极客good
关注
发布于: 刚刚

========


Gap Lock,翻译成间隙锁,他指的是在索引记录之间的间隙上的锁,或者在第一个索引记录之前或最后一个索引记录之后的间隙上的锁。


那么,这里所谓的 Gap(间隙)又怎么理解呢?


Gap 指的是 InnoDB 的索引数据结构中可以插入新值的位置。


当你用语句 SELECT…FOR UPDATE 锁定一组行时。InnoDB 可以创建锁,应用于索引中的实际值以及他们之间的间隙。例如,如果选择所有大于 10 的值进行更新,间隙锁将阻止另一个事务插入大于 10 的新值。



既然是锁,那么就可能会影响到数据库的并发性,所以,间隙锁只有在 Repeatable Reads 这种隔离级别中才会起作用。


在 Repeatable Reads 这种隔离下,对于锁定的读操作(select … for update 、 lock in share mode)、update 操作、delete 操作时,会进行如下的加锁:


  • 对于具有唯一搜索条件的唯一索引,InnoDB 只锁定找到的索引记录,而不会锁定间隙。

  • 对于其他搜索条件,InnoDB 锁定扫描的索引范围,使用 gap lock


【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


或 next-key lock 来阻塞其他事务插入范围覆盖的间隙。


也就是说,对于 SELECT FOR UPDATE、LOCK IN SHARE MODE、UPDATE 和 DELETE 等语句处理时,除了对唯一索引的唯一搜索外都会获取 gap 锁或 next-key 锁,即锁住其扫描的范围。


Next-Key Lock


=============


Next-Key 锁是索引记录上的记录锁和索引记录之前间隙上的间隙锁的组合。



假设一个索引包含值 10、11、13 和 20。此索引可能的 next-key 锁包括以下区间:


(-∞, 10]


(10, 11]


(11, 13]


(13, 20]


(20, ∞ ]


对于最后一个间隙,∞不是一个真正的索引记录,因此,实际上,这个 next-key 锁只锁定最大索引值之后的间隙。


所以,Next-Key 的锁的范围都是左开右闭的。


Next-Key Lock 和 Gap Lock 一样,只有在 InnoDB 的 RR 隔离级别中才会生效。


Repeatable Reads 能解决幻读


=====================


很多人看过网上的关于数据库事务级别的介绍,会认为 MySQL 中 Repeatable Reads 能解决不可重复读的问题,但是不能解决幻读,只有 Serializable 才能解决。但其实,这种想法是不对的。


因为 MySQL 跟标准 RR 不一样,标准的 Repeatable Reads 确实存在幻读问题,但 InnoDB 中的 Repeatable Reads 是通过 next-key lock 解决了 RR 的幻读问题的


因为我们知道,因为有了 next-key lock,所以在需要加行锁的时候,会同时在索引的间隙中加锁,这就使得其他事务无法在这些间隙中插入记录,这就解决了幻读的问题。


关于这个问题,引起过广泛的讨论,可以参考:https://github.com/Yhzhtk/note/issues/42 ,这里有很多大神发表过自己的看法。


MySQL 的加锁原则


==========


前面介绍过了 Record Lock、Gap Lock 和 Next-Key Lock,但是并没有说明加锁规则。关于加锁规则,我是看了丁奇大佬的《MySQL 实战 45 讲》中的文章之后理解的,他总结的加锁规则里面,包含了两个“原则”、两个“优化”和一个“bug”:


原则 1:加锁的基本单位是 next-key lock。是一个前开后闭区间。原则 2:查找过程中访问到的对象才会加锁。优化 1:索引上的等值查询,给唯一索引加锁的时候,next-key lock 退化为行锁。优化 2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁。一个 bug:唯一索引上的范围查询会访问到不满足条件的第一个值为止。


假如,数据库表中当前有以下记录:



当我们执行 update t set d=d+1 where id = 7 的时候,由于表 t 中没有 id=7 的记录,所以:


  • 根据原则 1,加锁单位是 next-key lock,session A 加锁范围就是 (5,10];

  • 根据优化 2,这是一个等值查询 (id=7),而 id=10 不满足查询条件,next-key lock 退化成间隙锁,因此最终加锁的范围是 (5,10)。


当我们执行 select * from t where id>=10 and id<11 for update 的时候:


  • 根据原则 1,加锁单位是 next-key lock,会给 (5,10]加上 next-key lock,范围查找就往后继续找,找到 id=15 这一行停下来

用户头像

极客good

关注

还未添加个人签名 2021.03.18 加入

还未添加个人简介

评论

发布
暂无评论
10个人9个答错,另外1个只对一半:数据库的锁