问一个答错一个:数据库的锁,到底锁的是什么?

10个人9个答错,另外1个只对一半:数据库的锁,到底锁的是什么?

在 MySQL 数据库中,为了解决并发问题,引入了很多的锁机制,很多时候,数据库的锁是在有数据库操作的过程中自动添加的。

所以,这就导致很多程序员经常会忽略数据库的锁机制的真正的原理。比如,我经常在面试中会问候选人,你知道 MySQL Innodb 的锁,到底锁的是什么吗?

关于这个问题的回答,我听到过很多种,但是很少有人可以把他回答的很完美。因为想要回答好这个问题,需要对数据库的隔离级别、索引等都有一定的了解才行。

MySQL Innodb 的锁的相关介绍,在 MySQL 的官方文档(MySQL :: MySQL 8.0 Reference Manual :: 15.7.1 InnoDB Locking )中有一定的介绍,本文的介绍也是基于这篇官方文档的。

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 操作时,会进行如下的加锁:

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

  • 对于其他搜索条件,InnoDB 锁定扫描的索引范围,使用 gap lock 或 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,所以在需要加行锁的时候,会同时在索引的间隙中加锁,这就使得其他事务无法在这些间隙中插入记录,这就解决了幻读的问题。

关于这个问题,引起过广泛的讨论,可以参考:Innodb 中 RR 隔离级别能否防止幻读? · Issue #42 · Yhzhtk/note · GitHub ,这里有很多大神发表过自己的看法。

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 这一行停下来

  • 根据优化 1,主键 id 上的等值条件,退化成行锁,只加了 id=10 这一行的行锁。

  • 根据原则 2,访问到的都要加锁,因此需要加 next-key lock(10,15]。因此最终加的是行锁 id=10 和 next-key lock(10,15]。

当我们执行 select * from t where id>10 and id<=15 for update 的时候:* 根据原则 1,加锁单位是 next-key lock,会给 (10,15]加上 next-key lock,并且因为 id 是唯一键,所以循环判断到 id=15 这一行就应该停止了。* 但是,InnoDB 会往前扫描到第一个不满足条件的行为止,也就是 id=20。而且由于这是个范围扫描,因此索引 id 上的 (15,20]这个 next-key lock 也会被锁上。

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

当我们执行 select id from t where c=5 lock in share mode 的时候:

  • 根据原则 1,加锁单位是 next-key lock,因此会给 (0,5]加上 next-key lock。要注意 c 是普通索引,因此仅访问 c=5 这一条记录是不能马上停下来的,需要向右遍历,查到 c=10 才放弃。

  • 根据原则 2,访问到的都要加锁,因此要给 (5,10]加 next-key lock。

  • 根据优化 2:等值判断,向右遍历,最后一个值不满足 c=5 这个等值条件,因此退化成间隙锁 (5,10)。

  • 根据原则 2 ,只有访问到的对象才会加锁,这个查询使用覆盖索引,并不需要访问主键索引,所以主键索引上没有加任何锁。

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

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

  • 根据原则 2,访问到的都要加锁,因此需要加 next-key lock(10,15]。

  • 由于索引 c 是非唯一索引,没有优化规则,也就是说不会蜕变为行锁,因此最终 sesion A 加的锁是,索引 c 上的 (5,10] 和 (10,15] 这两个 next-key lock。

总结

以上,我们介绍了 InnoDB 中的锁机制,一共有三种锁,分别是 Record Lock、Gap Lock 和 Next-Key Lock。

Record Lock 表示记录锁,锁的是索引记录。Gap Lock 是间隙锁,说的是索引记录之间的间隙。Next-Key Lock 是 Record Lock 和 Gap Lock 的组合,同时锁索引记录和间隙。他的范围是左开右闭的。

InnoDB 的 RR 级别中,加锁的基本单位是 next-key lock,只要扫描到的数据都会加锁。唯一索引上的范围查询会访问到不满足条件的第一个值为止。

同时,为了提升性能和并发度,也有两个优化点:

  • 索引上的等值查询,给唯一索引加锁的时候,next-key lock 退化为行锁。

  • 索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁。

关于锁的介绍,就是这么多了,但是其实,RR 的隔离级别引入的这些锁,虽然一定程度上可解决很多如幻读这样的问题,但是也会带来一些副作用,比如并发度降低、容易导致死锁等。

后面我们再来单独介绍一下为什么 RR 作为 InnoDB 的默认级别,却"不受待见",很多大厂都会把数据库默认级别修改为 RC。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 数据库的三级模式结构是什么? 答:数据库的三级模式结构是外模式、概念模式和内模式。外模式是用户能够看到和访的数据的逻辑视图,概念模式是整个数据库的逻辑结构,内模式是数据库的物理存储结构。 2. 什么是关系型数据库? 答:关系型数据库是以关系为基础的数据库,它采用表格来组织和存储数据,表格中的每一行代表一个记录,每一列代表一个属性。关系型数据库使用SQL语言来操作和查询数据。 3. 数据库的ACID是什么? 答:ACID是数据库事务的四个特性,包括原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。原子性指事务是不可分割的,要么全部完成,要么全部回滚;一致性指事务在执行前后数据库的状态必须保持一致;隔离性指多个事务可以并发执行,但相互之间不能干扰;持久性指一旦事务提交,对数据库的修改就是永久的。 4. 什么是索引? 答:索引是用来提高数据库查询效率的数据结构,它是一个特殊的表,其中包含了主要表的一部分数据以及对应的指针。通过索引,可以快速定位到符合条件的数据,从而加快查询速度。 5. 数据库机制是什么?并发控制是什么? 答:数据库机制是为了控制并发访数据库时的数据一致性和事务隔离性而设计的。机制包括共享和排它,共享用于读取数据,排它用于修改数据。并发控制是指在多个用户并发访数据库时,保证数据的一致性、可靠性和完整性的一种机制。并发控制包括两阶段、多版本并发控制等技术。 6. 什么是数据库的范式? 答:数据库的范式是一种设计规范,它用来规范数据库表的结构和关系,以保证数据的一致性和正确性。数据库的范式包括1NF(第一范式)、2NF(第二范式)、3NF(第三范式)等。其中,1NF要求表中的每个属性都是原子的;2NF要求表中的非主键属性必须完全依赖于主键;3NF要求表中的非主键属性必须不依赖于其他非主键属性。 7. 什么是数据库的事务? 答:数据库的事务是指一组数据库操作,这些操作要么全部执行成功,要么全部回滚。事务具有原子性、一致性、隔离性和持久性四个特性。事务可以通过BEGIN、COMMIT和ROLLBACK等命令来实现。 8. 什么是数据库的视图? 答:数据库的视图是一种虚拟的表格,它是由一个或多个表格的查询结果组成的。视图可以隐藏底层表格的结构和数据,只暴露需要的数据给用户。视图可以被用来简化复杂的查询、提供数据安全性和隔离性等功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值