MySQL串行化隔离级别(间隙锁实现)


Posted in MySQL onJune 16, 2022

串行化隔离级别怎么解决幻读问题?
先说下幻读的含义,幻读就是在事务中按照同样的条件前后两次查询的结果数据量不同。

MySQL串行化隔离级别(间隙锁实现)

解决串行化的幻读问题用间隙锁(gap lock),间隙锁是给不存在的记录加锁,要正确理解间隙,知道间隙的范围。条件无非就是两类:范围查询和等值查询。再说下范围查询和等值查询都是怎么加间隙锁的,分别从主键索引和辅助索引两个场景来说。

一、间隙锁的概念

MySQL串行化隔离级别(间隙锁实现)

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

record lock(记录锁,就是行锁)
gap lock(间隙锁)
next-key lock:record lock 和 gap lock

二、测试间隙锁范围加锁

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

MySQL串行化隔离级别(间隙锁实现)

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

MySQL串行化隔离级别(间隙锁实现)

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

做范围查询

MySQL串行化隔离级别(间隙锁实现)

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

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

MySQL串行化隔离级别(间隙锁实现)

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

12,22,23是三个行记录,因为过滤条件是用id带有索引的,所以select获取了12,22,23的共享行锁(record-lock), 还把间隙加了间隙锁,其实就是给间隙加上共享锁或者排他锁,将间隙锁和行锁统称next-key lock(record-lock和gap-lock),也就是说where id>11加了next-key lock。正是因为给空洞也加锁了,所以事务1再想获取间隙的排它锁是不可以的,因为共享锁和排它锁是不能共存的。

由于事务2是select,所以是给间隙加上了共享锁,如果事务1做select id>11还是可以的,不能update、insert、delete id>11的数据。

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

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

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

MySQL串行化隔离级别(间隙锁实现)

根据表的内容建简单的辅助索引

MySQL串行化隔离级别(间隙锁实现)

开启事务进行测试

MySQL串行化隔离级别(间隙锁实现)

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

MySQL串行化隔离级别(间隙锁实现)

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

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

MySQL串行化隔离级别(间隙锁实现)


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

辅助索引值相等的话。主键按升序排列。

MySQL串行化隔离级别(间隙锁实现)

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

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

MySQL串行化隔离级别(间隙锁实现)

回滚,重新开启事务

MySQL串行化隔离级别(间隙锁实现)

开始测试

MySQL串行化隔离级别(间隙锁实现)

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

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

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

MySQL串行化隔离级别(间隙锁实现)

果然,没有用到索引

MySQL串行化隔离级别(间隙锁实现)

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

三、测试间隙锁等值加锁

查看表结构和表数据

MySQL串行化隔离级别(间隙锁实现)

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

MySQL串行化隔离级别(间隙锁实现)

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

此时事务2做select操作,由于是等值查询,所以给这条数据加了共享锁。

MySQL串行化隔离级别(间隙锁实现)

事务2的主键或者唯一键进行等值查询的时候,事务1插入一个新的数据是可以成功的,因为主键id不能重复,我们不能再插入主键id=9的数据。

MySQL串行化隔离级别(间隙锁实现)

在这种情况下,主键或者唯一键是不能重复的,事务2进行等值查询时,事务1插入一个新的数据,不用担心这条数据和查询条件是一样的,所以肯定能成功

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

回滚并重启事务

MySQL串行化隔离级别(间隙锁实现)

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

MySQL串行化隔离级别(间隙锁实现)

这是一个等值查询,而且用的是辅助索引age,那么在辅助索引age的辅助索引树上叶子节点存的是age的辅助索引值和它所在行的主键值,

MySQL串行化隔离级别(间隙锁实现)

事务1插入age=18是不被允许的,否则事务2再查询age=18就有两条记录了。

MySQL串行化隔离级别(间隙锁实现)

奇怪的是,我们插入age=17,16,15也被阻塞住了

MySQL串行化隔离级别(间隙锁实现)

这是因为,为了防止幻读,除了age=18这条数据加了共享锁,其两侧也被加了间隙锁。

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

MySQL串行化隔离级别(间隙锁实现)

插入age=14,13都可以成功,不在间隙锁范围内。

MySQL串行化隔离级别(间隙锁实现)

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

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

到此这篇关于MySQL串行化隔离级别(间隙锁实现)的文章就介绍到这了,更多相关MySQL 间隙锁内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!


Tags in this post...

MySQL 相关文章推荐
jdbc使用PreparedStatement批量插入数据的方法
Apr 27 MySQL
MySQL主从搭建(多主一从)的实现思路与步骤
May 13 MySQL
解析MySQL binlog
Jun 11 MySQL
MySQL系列之十五 MySQL常用配置和性能压力测试
Jul 02 MySQL
一篇文章带你深入了解Mysql触发器
Aug 02 MySQL
MySQL分库分表详情
Sep 25 MySQL
Arthas排查Kubernetes中应用频繁挂掉重启异常
Feb 28 MySQL
Mysql排查分析慢sql之explain实战案例
Apr 19 MySQL
mysql查找连续出现n次以上的数字
May 11 MySQL
MySQL数据库安装方法与图形化管理工具介绍
May 30 MySQL
MySQL中order by的执行过程
Jun 05 MySQL
MySQL的表级锁,行级锁,排它锁和共享锁
Jul 15 MySQL
MySQL详解进行JDBC编程与增删改查方法
Jun 16 #MySQL
MySQL慢查询中的commit慢和binlog中慢事务的区别
Jun 16 #MySQL
MySQL聚簇索引和非聚簇索引的区别详情
关于mysql中string和number的转换问题
Jun 14 #MySQL
mysql实现将字符串字段转为数字排序或比大小
Jun 14 #MySQL
手把手带你彻底卸载MySQL数据库
MYSQL中文乱码问题的解决方案
Jun 14 #MySQL
You might like
JAVA/JSP学习系列之四
2006/10/09 PHP
PHP实现类似于C语言的文件读取及解析功能
2017/09/01 PHP
jQuery 获取对象 定位子对象
2010/05/31 Javascript
如何用js控制frame的隐藏或显示的解决办法
2013/03/20 Javascript
jquery ajax提交整个表单元素的快捷办法
2013/03/27 Javascript
写JQuery插件的基本知识
2013/11/25 Javascript
使用js判断数组中是否包含某一元素(类似于php中的in_array())
2013/12/12 Javascript
FF(火狐)浏览器无法执行window.close()解决方案
2014/11/13 Javascript
Angular实现form自动布局
2016/01/28 Javascript
关于动态生成dom绑定事件失效的原因及解决方法
2016/08/06 Javascript
jQuery实现移动端Tab选项卡效果
2017/03/15 Javascript
Node.js学习之查询字符串解析querystring详解
2017/09/28 Javascript
vue.js分页中单击页码更换页面内容的方法(配合spring springmvc)
2018/02/10 Javascript
koa上传excel文件并解析的实现方法
2018/08/09 Javascript
angularjs使用div模拟textarea文本框的方法
2018/10/02 Javascript
JS立即执行函数功能与用法分析
2019/01/15 Javascript
JS/jQuery实现获取时间的方法及常用类完整示例
2019/03/07 jQuery
Echarts实现多条折线可拖拽效果
2019/12/19 Javascript
JS document form表单元素操作完整示例
2020/01/13 Javascript
[47:22]Mineski vs Winstrike 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
python三元运算符实现方法
2013/12/17 Python
Python实现partial改变方法默认参数
2014/08/18 Python
Python中实现对list做减法操作介绍
2015/01/09 Python
Swift中的协议(protocol)学习教程
2016/07/08 Python
解决uWSGI的编码问题详解
2017/03/24 Python
python爬虫之模拟登陆csdn的实例代码
2018/05/18 Python
Python中反射和描述器总结
2018/09/23 Python
pytorch 实现张量tensor,图片,CPU,GPU,数组等的转换
2020/01/13 Python
AmazeUI中模态框的实现
2020/08/19 HTML / CSS
中学教师管理制度
2014/01/14 职场文书
读群众路线心得体会
2014/03/07 职场文书
迎新晚会主持词
2014/03/24 职场文书
迎国庆演讲稿
2014/09/15 职场文书
男方婚礼答谢词
2015/01/20 职场文书
研究生简历自我评
2015/03/11 职场文书
详解Python中的for循环
2022/04/30 Python