创意电子

标题: 10个人9个答错,另外1个只对一半:数据库的锁,到底锁的是什么? [打印本页]

作者: java互联网架构    时间: 2021-9-13 13:57
标题: 10个人9个答错,另外1个只对一半:数据库的锁,到底锁的是什么?
在MySQL数据库中,为了解决并发题目,引入了很多的锁机制,很多时候,数据库的锁是在有数据库操作的过程中自动添加的。
所以,这就导致很多程序员经常会忽略数据库的锁机制的真正的原理。比如,我经常在面试中会问候选人,你知道MySQL Innodb的锁,到底锁的是什么吗?
关于这个题目的答复,我听到过很多种,但是很少有人可以把他答复的很完美。因为想要答复好这个题目,需要对数据库的隔离级别、索引等都有一定的了解才行。
MySQL Innodb的锁的相关先容,在MySQL的官方文档(https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html#innodb-insert-intention-locks )中有一定的先容,本文的先容也是基于这篇官方文档的。

Record Lock

Record Lock,翻译成记录锁,是加在索引记录上的锁。例如,SELECT c1 FROM t WHERE c1 = 10 For UPDATE;会对c1=10这条记录加锁,为了防止任何其他事务插入、更新或删除c1值为10的行。

                               
登录/注册后可看大图

需要特殊注意的是,记录锁锁定的是索引记录。即使表没有定义索引,InnoDB也会创建一个隐藏的聚集索引,并使用这个索引来锁定记录。
Gap Lock

Gap Lock,翻译成间隙锁,他指的是在索引记录之间的间隙上的锁,大概在第一个索引记录之前或最后一个索引记录之后的间隙上的锁。
那么,这里所谓的Gap(间隙)又怎么理解呢?
Gap指的是InnoDB的索引数据结构中可以插入新值的位置。
当你用语句SELECT…FOR UPDATE锁定一组行时。InnoDB可以创建锁,应用于索引中的实际值以及他们之间的间隙。例如,如果选择全部大于10的值进行更新,间隙锁将阻止另一个事务插入大于10的新值。

                               
登录/注册后可看大图

既然是锁,那么就可能会影响到数据库的并发性,所以,间隙锁只有在Repeatable Reads这种隔离级别中才会起作用。
在Repeatable Reads这种隔离下,对于锁定的读操作(select … for update 、 lock in share mode)、update操作、delete操作时,会进行如下的加锁:

也就是说,对于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 的记录,所以:

<span style="letter-spacing: 1px;">当我们执行<span style="background-color: #F8F8F8; --tt-darkmode-bgcolor: #191919;">select * from t where id>=10 and id10 and id=10 and c
作者: 花季年华    时间: 2021-9-13 19:24
转发了
作者: 老王998673    时间: 2021-9-13 23:58
转发了
作者: 沐紫剑    时间: 2021-9-14 00:20
转发了
作者: 郝剑大侠    时间: 2021-9-14 12:34
转发了
作者: laoli331    时间: 2021-9-14 22:39
转发了
作者: 进步加菲尔德7J2    时间: 2021-9-15 08:02
转发了
作者: 敏行勤思慎言    时间: 2021-9-15 11:33
转发了
作者: 萧翎翼    时间: 2021-9-16 08:28
转发了
作者: 用户tl    时间: 2021-9-16 09:07
转发了
作者: 愤怒小汤圆圆    时间: 2021-9-16 09:12
转发了
作者: 半月弦本尊    时间: 2021-9-16 09:23
转发了
作者: 外向晚风eB    时间: 2021-9-16 10:55
问题来了,正确答案是哪里来的?
作者: 优选狮子头    时间: 2021-9-17 07:31
转发了
作者: 马里Giao    时间: 2021-9-17 15:38
转发了
作者: 关关雎鸠35    时间: 2021-9-20 01:29
转发了
作者: 二等分Pro    时间: 2021-9-20 07:26
转发了
作者: yau同志仍需努力    时间: 2021-10-23 17:19
转发了
作者: 老王998673    时间: 2021-12-6 23:40
转发了
作者: 老王998673    时间: 2021-12-6 23:49
转发了




欢迎光临 创意电子 (https://www.wxcydz.cc/) Powered by Discuz! X3.4