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 相关文章推荐
详解MySQL数据库千万级数据查询和存储
May 18 MySQL
MySQL系列之十二 备份与恢复
Jul 02 MySQL
MySQL中连接查询和子查询的问题
Sep 04 MySQL
SQL 聚合、分组和排序
Nov 11 MySQL
MySQL中CURRENT_TIMESTAMP的使用方式
Nov 27 MySQL
分享MySQL常用 内核 Debug 几种常见方法
Mar 17 MySQL
分享几个简单MySQL优化小妙招
Mar 31 MySQL
详解MySQL的主键查询为什么这么快
Apr 03 MySQL
MySQL三种方式实现递归查询
Apr 18 MySQL
MySQL 数据库 增删查改、克隆、外键 等操作
May 11 MySQL
Mysql开启外网访问
May 15 MySQL
MySQL下载安装配置详细教程 附下载资源
Sep 23 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
php下一个阿拉伯数字转中文数字的函数
2007/07/16 PHP
php str_pad() 将字符串填充成指定长度的字符串
2010/02/23 PHP
PHP持久连接mysql_pconnect()函数使用介绍
2012/02/05 PHP
PHP遍历文件夹与文件类及处理类用法实例
2014/09/23 PHP
Yii入门教程之Yii安装及hello world
2014/11/25 PHP
Laravel相关的一些故障解决
2020/08/19 PHP
学习ExtJS table布局
2009/10/08 Javascript
HTML Dom与Css控制方法
2010/10/25 Javascript
jQuery 常见操作实现方式和常用函数方法总结
2011/05/06 Javascript
JavaScript中实现异步编程模式的4种方法
2014/09/24 Javascript
js实现网页右上角滑出会自动消失大幅广告的方法
2015/02/27 Javascript
JavaScript来实现打开链接页面的简单实例
2016/06/02 Javascript
实例解析jQuery中如何取消后续执行内容
2016/12/01 Javascript
Bootstrap CSS组件之按钮下拉菜单
2016/12/17 Javascript
微信小程序 slider的简单实例
2017/04/19 Javascript
jQuery之动画ajax事件(实例讲解)
2017/07/18 jQuery
修改 bootstrap table 默认detailRow样式的实例代码
2017/07/21 Javascript
微信小程序url与token设置详解
2017/09/26 Javascript
解决vue-cli + webpack 新建项目出错的问题
2018/03/20 Javascript
Angular模版驱动表单的使用总结
2018/05/05 Javascript
js实现窗口全屏示例详解
2019/09/17 Javascript
react国际化化插件react-i18n-auto使用详解
2020/03/31 Javascript
vue+iview框架实现左侧动态菜单功能的示例代码
2020/07/23 Javascript
解决pycharm回车之后不能换行或不能缩进的问题
2019/01/16 Python
python对Excel的读取的示例代码
2020/02/14 Python
pytorch实现Tensor变量之间的转换
2020/02/17 Python
jupyter notebook 多环境conda kernel配置方式
2020/04/10 Python
python 如何利用argparse解析命令行参数
2020/09/11 Python
纽约的奢华内衣店:Journelle
2016/07/29 全球购物
日本钓鱼渔具和户外用品网上商店:naturum
2016/08/07 全球购物
在阿尔卑斯山或希腊度过快乐假期:Alpine Elements
2019/12/28 全球购物
如何用Lucene索引数据库
2016/02/23 面试题
股权转让协议书
2014/04/12 职场文书
餐厅周年庆活动方案
2014/08/25 职场文书
2014领导班子四风问题查摆思想汇报
2014/09/13 职场文书
在Django中使用MQTT的方法
2021/05/10 Python