MySQL间隙锁(幻读解决原理)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: MySQL间隙锁(幻读解决原理)

一、间隙锁概念

  • 当我们用范围条件而不是相等条件检索数据, 并请求共享或排他锁时,InnoDB 会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)” ,InnoDB 也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁
  • 举例来说, 假如 user 表中只有 101 条记录, 其userid 的值分别是 1,2,…,100,101, 下面的 SQL: select * from user where userid > 100 for update;是一个范围条件的检索,InnoDB 不仅会对符合条件的 userid 值为 101 的记录加锁,也会对userid 大 于 101(但是这些记录并不存在)的"间隙"加锁,防止其它事务在表的末尾增加数据
  • InnoDB 使用间隙锁的目的,是为了防止幻读,以满足串行化隔离级别的要求 ,对于上面的例子,要是不使用间隙锁,如果其他事务插入了 userid 大于 100 的任何记录,那么本事务如果再次执行上述语句,就会发生幻读

InnoDB串行化隔离级别使用间隙锁(gap lock)解决幻读(事务并发情况下两次查询的数据量不同)问题

间隙锁专用于串行化隔离级别,可解决幻读问题,幻读问题表现为:当前事务没做操作,前后两次相同的查询语句,显示的数据量不一致

image.png

我们把事务2 select的指定的条件分为2类:范围查询、等值查询

record lock:记录锁,就是行锁

gap lock:间隙锁,不包含记录本身

next-key lock:record lock(记录本身) + gap lock(不包括记录本身)

二、测试间隙锁范围加锁

设置事务为手动提交,然后把隔离级别设置成串行化

image.png

查看表结构,id、age、name都有索引

image.png

场景1:用不可重复的主键id测试间隙锁

范围查询

image.png

事务2的select操作只给三行数据加了共享锁,为什么插入id为24的数据也不行呢?

这是因为在串行化隔离级别中,不仅仅是获取了满足条件的这3行的行锁,而且把表记录之间以及后边空洞的地方也加上了间隙锁

image.png

图中红色线的地方都上了next-key锁,上锁范围(左开右闭)为:( 11 , 12 ] ∪ ( 12 , 22 ] ∪ ( 22 , 23 ] ∪ ( 23 , + ∞ ]

上述select不仅仅获取了12,22,23的共享行锁(record-lock),还把间隙加了间隙锁,其实就是给间隙加上共享锁或者排他锁,由于我们这里是select,所以是给间隙加上了共享锁(我们select id>11还是可以的,不能update、insert、delete id>11的数据)

也就是说,我们可以在id小于11的地方update/delete,加排它锁。但是操作了id<=11的部分,不影响相同的select * from stu where id>11所获取的数据量,这样就能防止幻读发生

串行化隔离级别通过给select的部分加间隙锁,防止其他事务在加了间隙锁的区间进行增加或删除数据,就能防止幻读

场景2:用可重复的age(有索引)测试间隙锁

测试辅助索引树上,间隙锁的范围

我们先查看一下表结构、表数据,然后回滚

image.png

开启事务进行测试

image.png

很明显,由于age>20的区间都被事务1加上了间隙锁(这里加的是共享锁),所以事务2插入age=22和age=21都失败了

image.png

幻读就是同一事务两次用相同的条件查询数据,下一次查出的数据量和上一次的数据量不一样,就算事务1把age=20的数据插入表,事务2再用age>20查询,得到的数据量也不会改变。

那事务1插入age=20的数据能否成功呢?

image.png

依然不能成功,这是因为我们插入的数据id是自增的,所以这条数据(age=20,id=24),位于辅助索引树中(age=20,id=12)的右边,由于(age=20,id=12)右边都被上了锁,(age=20,id=24)自然无法插入

也就是说,如果我们指定age=20,id合法且<12,则可插入

image.png

很显然,事务1插入的age=18和age=19都不在事务2上锁的范围,所以可以插入

如果只是在辅助索引树上查找,不回表,那么主键索引树上不会加锁

image.png

image.png

select id from stu where age>14后,辅助索引树加锁区间如下:

image.png

image.png

场景3:实际情况需要具体分析用的到底是行锁还是表锁

image.png

回滚,重新开启事务

image.png

开始测试

image.png

我们发现事务1无论是插入age>18范围内的数据,还是范围外的数据,都无法成功

这时我们就要分析了,这应该没有用到索引,因为我们用索引,过滤出的数据占了整张表的一大半,MySQL server没使用索引。

没有加行锁,只能加表锁(这时加的是共享锁),所以事务1无论插入什么数据都不行

image.png

果然,没有用到索引

image.png

age>20用到了索引,所以可以用行锁

三、测试等值间隙锁

查看表结构和表数据

image.png

设置成手动提交,设置串行化隔离级别。回滚,然后启动事务

image.png

1. 测试不能重复的主键索引

此时事务2 select,由于是等值查询,相当于给这条数据加上了共享锁

image.png

事务1现在插入新的数据是可以成功的,因为主键id不能重复,我们不能再插入主键id=9的数据

image.png

在这种情况下,由于id=9的数据已经存在,主键和唯一键是不能重复的,事务2进行等值查询时,事务1插入一个新的数据,不用担心这条新插入的数据和查询条件是一样的,如果主键一样,SQL语句执行失败,所以肯定能成功

2. 测试能重复的辅助索引

回滚并重启事务

image.png

事务2等值查询,给age=18这行数据加上共享锁(record-lock)

image.png

image.png

事务1插入age=18,这是不能允许的,否则事务2再查询age=18就有两条记录了(幻读)

image.png

奇怪的是,我们插入age=17、16、15都被阻塞了,而14、13成功了

image.png

这是因为,为了防止幻读,除了age=18这条数据加了共享锁,其两侧也被加上了间隙锁,因为在这种情况下,插入(age=18,id=10)和(age=18,id=8)是会发生幻读的,所以在一切会影响select * from user where age=18查询结果的地方都加上了间隙锁,但这也会导致一些本不影响查询结果的语句也执行失败,比如插入(age=17,id=24)虽然不影响上述SQL执行结果,由于在间隙锁范围内,依然无法插入

如果插入(age=15,id=1)就可以成功,根据辅助索引值相同,按照主键值升序排列,(age=15,id=1)应该放在(age=15,id=23)前面,不在间隙锁范围内

image.png

间隙锁是给不存在的数据记录的范围加锁:

  • 对于辅助索引,若值允许重复,在串行隔离级别中如果进行等值查询,InnoDB会给数据加上record-lock和gap-lock(防止别的事务插入索引值重复的数据,造成幻读)
  • 对于主键索引,或者唯一键索引,值不允许重复,那只需要加行锁就够了,不需要再加间隙锁(对于唯一键索引,不可能发生插入索引值重复的数据)

串行化隔离级别通过排它锁和共享锁解决脏读、不可重复读(两次查询的数据内容不同),通过间隙锁解决幻读(两次查询的数据量不同)



相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
2月前
|
SQL 关系型数据库 MySQL
MySQL 锁
MySQL里常见的几种锁
56 3
|
9天前
|
SQL 存储 关系型数据库
MySQL进阶突击系列(01)一条简单SQL搞懂MySQL架构原理 | 含实用命令参数集
本文从MySQL的架构原理出发,详细介绍其SQL查询的全过程,涵盖客户端发起SQL查询、服务端SQL接口、解析器、优化器、存储引擎及日志数据等内容。同时提供了MySQL常用的管理命令参数集,帮助读者深入了解MySQL的技术细节和优化方法。
|
2月前
|
存储 关系型数据库 MySQL
MySQL主从复制原理和使用
本文介绍了MySQL主从复制的基本概念、原理及其实现方法,详细讲解了一主两从的架构设计,以及三种常见的复制模式(全同步、异步、半同步)的特点与适用场景。此外,文章还提供了Spring Boot环境下配置主从复制的具体代码示例,包括数据源配置、上下文切换、路由实现及切面编程等内容,帮助读者理解如何在实际项目中实现数据库的读写分离。
MySQL主从复制原理和使用
|
2月前
|
缓存 算法 关系型数据库
Mysql(3)—数据库相关概念及工作原理
数据库是一个以某种有组织的方式存储的数据集合。它通常包括一个或多个不同的主题领域或用途的数据表。
69 5
Mysql(3)—数据库相关概念及工作原理
|
2月前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1674 14
|
2月前
|
SQL 关系型数据库 MySQL
Mysql中搭建主从复制原理和配置
主从复制在数据库管理中广泛应用,主要优点包括提高性能、实现高可用性、数据备份及灾难恢复。通过读写分离、从服务器接管、实时备份和地理分布等机制,有效增强系统的稳定性和数据安全性。主从复制涉及I/O线程和SQL线程,前者负责日志传输,后者负责日志应用,确保数据同步。配置过程中需开启二进制日志、设置唯一服务器ID,并创建复制用户,通过CHANGE MASTER TO命令配置从服务器连接主服务器,实现数据同步。实验部分展示了如何在两台CentOS 7服务器上配置MySQL 5.7主从复制,包括关闭防火墙、配置静态IP、设置域名解析、配置主从服务器、启动复制及验证同步效果。
Mysql中搭建主从复制原理和配置
|
2月前
|
存储 关系型数据库 MySQL
优化 MySQL 的锁机制以提高并发性能
【10月更文挑战第16天】优化 MySQL 锁机制需要综合考虑多个因素,根据具体的应用场景和需求进行针对性的调整。通过不断地优化和改进,可以提高数据库的并发性能,提升系统的整体效率。
103 1
|
2月前
|
SQL 关系型数据库 MySQL
阿里面试:MYSQL 事务ACID,底层原理是什么? 具体是如何实现的?
尼恩,一位40岁的资深架构师,通过其丰富的经验和深厚的技術功底,为众多读者提供了宝贵的面试指导和技术分享。在他的读者交流群中,许多小伙伴获得了来自一线互联网企业的面试机会,并成功应对了诸如事务ACID特性实现、MVCC等相关面试题。尼恩特别整理了这些常见面试题的系统化解答,形成了《MVCC 学习圣经:一次穿透MYSQL MVCC》PDF文档,旨在帮助大家在面试中展示出扎实的技术功底,提高面试成功率。此外,他还编写了《尼恩Java面试宝典》等资料,涵盖了大量面试题和答案,帮助读者全面提升技术面试的表现。这些资料不仅内容详实,而且持续更新,是求职者备战技术面试的宝贵资源。
阿里面试:MYSQL 事务ACID,底层原理是什么? 具体是如何实现的?
|
2月前
|
存储 关系型数据库 MySQL
MySQL锁,锁的到底是什么?
【10月更文挑战第16天】MySQL 锁锁定的是与数据和资源相关的对象,其目的是为了保证数据的一致性、避免冲突,并在并发环境下合理协调事务或操作的执行。理解锁的对象和意义对于优化数据库性能、处理并发问题至关重要。
75 0
|
2月前
|
关系型数据库 MySQL 数据库
mysql锁详解
通过理解并合理运用MySQL中的锁机制,开发者可以有效管理数据库并发访问,平衡性能与数据一致性需求。更多关于MySQL锁的深入探讨和最佳实践,请参考专业的数据库管理资源[[深入MySQL锁机制详解
50 0