10个人有9个答错,另外1个只对一半:数据库的锁,到底锁的是什么?
△Hollis, 一个对Coding有着独特追求的人△
这是Hollis的第 373 篇原创分享
作者 l Hollis
来源 l Hollis(ID:hollischuang)
在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操作时,会进行如下的加锁:
对于具有唯一搜索条件的唯一索引,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,所以在需要加行锁的时候,会同时在索引的间隙中加锁,这就使得其他事务无法在这些间隙中插入记录,这就解决了幻读的问题。
关于这个问题,引起过广泛的讨论,可以参考: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 这一行停下来
根据优化 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。
技术交流群
最近有很多人问,有没有读者交流群,想知道怎么加入。
最近我创建了一些群,大家可以加入。交流群都是免费的,只需要大家加入之后不要随便发广告,多多交流技术就好了。
目前创建了多个交流群,全国交流群、北上广杭深等各地区交流群、面试交流群、资源共享群等。
有兴趣入群的同学,可长按扫描下方二维码,一定要备注:全国 Or 城市 Or 面试 Or 资源,根据格式备注,可更快被通过且邀请进群。
▲长按扫描
往期推荐
终于把公司的底裤扒了!
李国庆:建议被降级降薪员工主动辞职……网友炸了!
假如一个程序员有"社交牛逼症"
如果你喜欢本文,
请长按二维码,关注 Hollis.
转发至朋友圈,是对我最大的支持。
点个 在看
喜欢是一种感觉
在看是一种支持
↘↘↘
10个人有9个答错,另外1个只对一半:数据库的锁,到底锁的是什么?相关推荐
- 德国80%的统计学教授都会答错的6个与P值有关的问题!
欢迎关注"R语言和统计"~~ 在2022年的第2天,小编阅读了一篇发表于2002年关于P值的一项问卷调查研究 [1],作者在6所德国大学中邀请了3组不同的受试者,分别为: 心理学专 ...
- 谷歌AI聊天机器人Bard答错问题,股价大跌7.4%;淘宝屏蔽ChatGPT;孟晚舟4月将首次当值华为轮值董事长丨每日大事件...
数据智能产业创新服务媒体 --聚焦数智 · 改变商业 投融资 深氧科技获千万元级天使轮融资 3D短视频的一站式AIGC引擎技术服务提供商深氧科技于2月8日宣布完成由汉能创投投资的千万元级天使 ...
- 替换元素_80%的前端会答错的问题:lt;imggt;是什么元素?
前言 某天晚上,和几个朋友去撸串,突然就聊到了面试,都在感叹现在的面试题太变态了,其中一个突然很神秘的问我:"你写前端这么久了,那你知道 <img> 是什么元素吗?" ...
- delphi项目开发经验2008年09月18日 星期四 10:07随着项目的失败,这些天一直在总结失败的原因,到底是为什么?
delphi项目开发经验 2008年09月18日 星期四 10:07 随着项目的失败,这些天一直在总结失败的原因,到底是为什么? 一.技术层面 1.少用指针类型,多用类. ...
- create 执行存储过程报错出现符号_记一次数据库迁移的过程采坑过程
业务场景 最近的一个项目最开始由于资源问题,mysql 数据库是部署在一台云服务器上的,这两天客户提供了云数据库,所以原来在部署在 ECS 服务器上的数据库,需要迁移到云数据库.在云数据库上的优势很多 ...
- 10年后端开发程序员详解数据库缓存方案到底有多少名堂。丨Linux服务器开发丨后端开发丨中间件丨web服务器丨数据库缓存
数据库缓存方案到底有多少花样,一节课带你缕清 1. 读写分离方案 2. 若干个缓存解决方案 3. 缓存故障如何解决 视频讲解如下,点击观看: 10年后端开发程序员详解数据库缓存方案到底有多少名堂.丨L ...
- java系统随机产生10道加法运算,用户进行答题,答对一道题加10分,答错不加分
import java.util.Scanner; public class Add {public static void main(String[] args) {//获取键盘输入Scanner ...
- oracle10.2.0.5漏洞,Oracle 10.2.0.5 opatch报错
Linux redhat 5 Oracle 10.2.0.5 执行opatch lsinventory报错: [oracle@rac2 8350262]$ opatch lsinventory Inv ...
- 80%的人答错,苹果logo上的叶子到底朝左还是朝右?
苹果手机大家应该都有了解,但是你能区分出它真正的logo吗?本文主要介绍运用Python中的turtle库控制函数绘制苹果logo和相似图案,据说80%的人都选错了. 一.效果展示 ...
最新文章
- UART的16倍频过采样和3倍频过采样
- Java截取最后一个/后面的所有字符
- 设计模式--代理(Proxy)模式
- 《廖雪峰 . Git 教程》学习总结
- 深度学习-机器学习(5.2支持向量机SVM上的Python应用)
- VTK:可视化之QuadricVisualization
- 客户想你死系列,哈哈哈设计师不容易啊! | 今日最佳
- layui绑定json_JSON绑定:概述系列
- uTorrent for mac(BT下载客户端)v1.8.7中文版
- 三、java语法基础
- 漂亮html表格页面模板,四款好看实用的CSS表格样式分享
- 怎么把matlab的背景改成白色背景图片,如何把图片背景换成白色?
- 【单片机竞赛:共阳数码管静态控制】
- python与开源gis_Python与开源GIS:SpatiaLite简介
- 习题--答案--22/6/8
- 短消息类新旧服务代码对应表
- ubuntu显卡输出hdmi屏幕没有声音
- little endian c语言,c语言那些细节之little-endian和big-endian
- 动态规划-试题(1)-扔玻璃珠
- oracle资产负债表重分类吗,​资产负债表一般是重分类还是不重分类
热门文章
- mysql常见数据库设计_常见数据库设计
- 闭式系统蒸汽管径推荐速度_空调水系统设计、空调风系统设计要点
- c# sha1签名 微信_C#微信公众号JS接口签名算法
- 计算机里的文件丢失6,丢失的文件怎么恢复?腾讯电脑管家恢复电脑丢失文件的方法介绍...
- 前端基础-html-水平线标签
- 操作系统之进程管理:18、预防死锁
- (软件工程复习核心重点)第十章面向对象设计-第二节:启发规则和软件重用
- (王道408考研操作系统)第四章文件管理-第二节1:磁盘的结构
- 2-2:套接字(Socket)编程之深入了解套接字
- 二叉树最大深度:给定一个二叉树,找出其最大深度。 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。