数据库的锁,到底锁的是什么?

  • 前言
  • Record Lock
  • Gap Lock
  • Next-Key Lock
  • Repeatable Reads能解决幻读
  • MySQL的加锁原则
  • 总结

前言

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

参照文章:MySQL 官方文档

Record Lock

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

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

Gap Lock

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

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 的锁的范围都是左开右闭的。
Next-Key Lock和Gap Lock一样,只有在InnoDB的Repeatable Reads隔离级别中才会生效。

Repeatable Reads能解决幻读

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

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

因为我们知道,有了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:唯一索引上的范围查询会访问到不满足条件的第一个值为止。

例子1,数据库表中当前有以下记录

当我们执行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 也会被锁上。

例子2,数据库表中当前有以下记录

当我们执行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 LockGap LockNext-Key Lock

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

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

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

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

数据库的锁,到底锁的是什么?相关推荐

  1. 【转】【MySQL】事务与锁(四):行锁到底锁住的是什么?记录?字段?索引?

    首先我们有三张表t1,t2,t3,它们都是只有两个字段, int类型的id和varchar类型的name:区别是t1没有索引,t2有主键索引,t3有唯一索引. 再强调一次,在实验前必须提前关闭自动提交 ...

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

    △Hollis, 一个对Coding有着独特追求的人△ 这是Hollis的第 373 篇原创分享 作者 l Hollis 来源 l Hollis(ID:hollischuang) 在MySQL数据库中 ...

  3. 基于 Redis 的分布式锁到底安全吗?

    [完整版] 网上有关Redis分布式锁的文章可谓多如牛毛了,不信的话你可以拿关键词"Redis 分布式锁"随便到哪个搜索引擎上去搜索一下就知道了.这些文章的思路大体相近,给出的实现 ...

  4. 基于Redis的分布式锁到底安全吗(上)?

    网上有关Redis分布式锁的文章可谓多如牛毛了,不信的话你可以拿关键词"Redis 分布式锁"随便到哪个搜索引擎上去搜索一下就知道了.这些文章的思路大体相近,给出的实现算法也看似合 ...

  5. 基于Redis的分布式锁到底安全吗?

    网上有关Redis分布式锁的文章可谓多如牛毛了,不信的话你可以拿关键词"Redis 分布式锁"随便到哪个搜索引擎上去搜索一下就知道了.这些文章的思路大体相近,给出的实现算法也看似合 ...

  6. Mysql数据库中的各种锁

    目录 概述 MyIsam 如何加表锁 并发锁 MyISAM的锁调度 InnoDB 事务(Transaction)及其ACID属性 并发事务带来的问题 InnoDB都有哪些锁? InnoDB的行锁模式及 ...

  7. oracle账号密码修改后特别容易锁定_Oracle数据库账号总是被锁?通过这里就可以发现是哪个IP造成的...

    概述 简单说下背景,公司对数据库密码复杂度有一定要求,所以需要改数据库密码,因为涉及到应用这块要对应修改,但运维和开发人员却不知道具体有哪些地方配置了数据库账号密码,导致数据库账号总是被锁,所以需要看 ...

  8. 深度剖析:Redis分布式锁到底安全吗?看完这篇文章彻底懂了!

    ‍‍‍‍‍‍‍‍‍‍‍‍阅读本文大约需要 20 分钟. 大家好,我是 Kaito. 这篇文章我想和你聊一聊,关于 Redis 分布式锁的「安全性」问题. Redis 分布式锁的话题,很多文章已经写烂了 ...

  9. 分布式锁,进程锁,线程锁到底是什么

    在分布式集群系统的开发中,线程锁往往并不能支持全部场景的使用,必须引入新的技术方案分布式锁. 线程锁:大家都不陌生,主要用来给方法.代码块加锁.当某个方法或者代码块使用锁时,那么在同一时刻至多仅有有一 ...

最新文章

  1. 20210712未来智能实验室收录资料
  2. 如何实现搜索列表_图解:如何理解与实现散列表
  3. linux删掉文件怎么恢复,linux系统误删除文件怎么恢复
  4. WCBuffer合并写
  5. 软件开发的核心是技术人员吗?
  6. python导入csv报错_Python Pandas read_csv报错
  7. oracle中with的用法及用处
  8. 爆料称小米平板5即将送网备案
  9. verilog学习记(开头篇)
  10. matlab 手工实现normalize函数 未定义与 ‘double‘ 类型的输入参数相对应的函数 ‘normalize‘
  11. HUT-XXXX 数学
  12. 如何修改Maven本地仓库位置
  13. 经典CNN图像分类网络汇总
  14. 蓝桥杯第四届初赛“模拟智能灌溉系统”设计任务书
  15. 工作到底都能给你带来哪些好处?
  16. win7开启超级管理员账户(Administrator)
  17. geoserver制作离线地图
  18. Python实战采集全球疫情数据
  19. vue入门(二)---vue实例
  20. 网络打印机不能安装驱动解决方案

热门文章

  1. vscode和visualstudio的区别?
  2. 第三章 嵌入式Python概述(三)
  3. 人工智能的最大危险是什么?
  4. 微信分享:后端参数获取PHP实现
  5. 2018——测试与信仰
  6. 【论文写作––课程总结】
  7. 2050年人类将“永生”?或是人工智能下的必然
  8. HTML-1-基础练习
  9. ESP32学习】驱动ws2812
  10. 宏基笔记本 ATI 显卡卡死问题的解决办法