生成间隙(gap)锁、临键(next-key)锁的前提条件 是在 RR 隔离级别下。

有关Mysql记录锁、间隙(gap)锁、临键锁(next-key)锁的一些理论知识之前有写过,详细内容可以看这篇文章 一文详解MySQL的锁机制

这篇主要通过小案例来对记录锁间隙(gap)锁临键(next-key)锁做一个更好的理解。

这里先给出结论,再来用实际例子证明

1、当使用唯一索引来等值查询的语句时, 如果这行数据存在,不产生间隙锁,而是记录锁。

2、当使用唯一索引来等值查询的语句时, 如果这行数据不存在,会产生间隙锁。

3、当使用唯一索引来范围查询的语句时,对于满足查询条件但不存在的数据产生间隙(gap)锁,如果查询存在的记录就会产生记录锁,加在一起就是临键锁(next-key)锁。

4、当使用普通索引不管是锁住单条,还是多条记录,都会产生间隙锁;

5、在没有索引上不管是锁住单条,还是多条记录,都会产生表锁;

间隙锁会封锁该条记录相邻两个键之间的空白区域,防止其它事务在这个区域内插入、修改、删除数据,这是为了防止出现 幻读 现象;

间隙的范围?

根据检索条件向下寻找最靠近检索条件的记录值A作为左区间,向上寻找最靠近检索条件的记录值B作为右区间,即锁定的间隙为(A,B] 左开右闭。

接下来我们开始来验证以上结论

一、数据和环境准备

1、创建表和数据

CREATE TABLE `t` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
`age` int NOT NULL COMMENT '年龄',
`mobile` int DEFAULT NULL COMMENT '手机号',
`name` varchar(8) DEFAULT NULL COMMENT '名称',
PRIMARY KEY (`id`),
KEY `index_age` (`age`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;

id为主键(唯一索引)、age是普通索引、mobile没有加索引

同时插入数据如下。

在进行测试之前,我们先来看看t表中存在的隐藏间隙:

(-∞, 1]

(1, 4]

(4, 7]

(7, +supernum]

(其中supernum是数据库维护的最大的值。为了保证间隙锁都是左开右闭原则。)

2、关闭事务默认提交

mysql> SHOW VARIABLES LIKE 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.00 sec)

结果显示,autocommit 的值是 ON,表示系统开启自动提交模式。

在 MySQL 中,可以使用 SET autocommit 语句设置事务的自动提交模式,语法格式如下:

SET autocommit = 0|1|ON|OFF;

对取值的说明:

值为 0 和值为 OFF:关闭事务自动提交。如果关闭自动提交,用户将会一直处于某个事务中,只有提交或回滚后才会结束当前事务,重新开始一个新事务。

值为 1 和值为 ON:开启事务自动提交。如果开启自动提交,则每执行一条 SQL 语句,事务都会提交一次。

二、唯一索引示例

1、等值查询且数据存在示例

事务A 等值查询id=4,因为id是主键,同时是等值查询存在该记录,所以只会在id=4这条记录上加记录锁,不会加间隙锁。

事务B 等值查询id=5,没有锁冲突,所以查询正常,不会堵塞。(如果事务B 等值查询id=4,因为事务A加了记录锁,所以会堵塞)

2、等值查询且数据不存在示例

事务A 等值查询id=5,因为查询记录不存在,所以无法加记录锁,但这里会存在一个(5,7]的间隙锁。

事务B 插入一条id=6的数据,因为上面存在了(5,7]的间隙锁,所以会堵塞。

3、范围查询示例

事务A 范围查询id>4,那么这里就会存在一个(4,+supernum]的临键(next-key)锁。

事务B 插入一条id=6的数据,因为上面存在了(4,+supernum]的临键(next-key)锁,所以会堵塞。

如果 事务B 是更新 id=7 的记录,同样会堵塞。

三、普通索引示例

1、等值查询值

事务A 等值查询age=4,因为age是普通索引,所以会产生临键(next-key)锁(1,4]和(4,7],左开右闭原则

事务B 插入一个id=6、age=6的数据,因为age值在上面临键锁,范围内,所以也会堵塞。

2、左开右闭原则

按照上面的例子,如果事务B插入一条 id=6,age=1 的数据会不会堵塞呢,因为按照左开右闭原则,上面的age=1是开的,所以正常应该是可以插入的。

但实际上你真是实践之后,你发现同样也会堵塞。

通过实践之后,会发现,所谓的左开右闭原则,跟主键id有关系。

上面的事务A 等值查询age=4,它的当前主键id=4,上一条记录主键id=1,下条记录主键id=7。

如果插入 id<1, age 在(1,7)范围内,是 左闭右开原则。即age=1能插入,age=7会堵塞。

如果插入 1<id<7,age 在(1,7)范围内,是 左闭右闭原则。即age=1会堵塞,age=7也会堵塞。

如果插入 id>7,age 在(1,7)范围内,是 左开右闭原则。即age=1会堵塞,age=7能插入。

有关等值查询值不存在、普通索引范围的示例这里就不举了,跟上面的差不多,都会产生间隙锁。

四、无索引示例

1、等值查询值

事务A 等值查询 mobile = 8888884,因为mobile是无索引的,所以这个for update,变成表级排他(X)锁。

事务B 因为事务A已经加了表级的排他锁,所以其它事务无法进行任何的增删改操作。

2、范围查询

事务A 等值查询 mobile > 8888884,因为mobile是无索引的,所以这个for update,变成表级排他(X)锁。

事务B 因为事务A已经加了表级的排他锁,所以其它事务无法进行任何的增删改操作。

最新文章

  1. Leetcode: Find Leaves of Binary Tree
  2. Thinkphp源码分析系列(四)–Dispatcher类
  3. ok6410,mmu,内存管理
  4. Jquery实现ready()的源码
  5. SHELL 八大扩展
  6. 关于c#字典key不存在的测试
  7. [Unity菜鸟] 协程Coroutine
  8. jquery serialize()方法的扩展
  9. 最简便的MySql数据库备份方法
  10. suse linux编译安装GCC报错
  11. YYCache 源码分析(一)
  12. try{...} catch {...} finally{...} 各种情况代码的执行情况
  13. DAY16、模块和包
  14. Js数组去重方法总结
  15. 开发环境搭建(lnmp)
  16. MAC book 无法删除普通用户的解决办法
  17. hdu 1.3.3 今年暑假不AC
  18. Alpha冲刺阶段博客汇总
  19. bzoj 2342 [Shoi2011]双倍回文(manacher,set)
  20. Python的日志记录-logging模块的使用

热门文章

  1. Part 37 Difference between $scope and $rootScope
  2. MySQL 各版本连接驱动字符串
  3. Django 小实例S1 简易学生选课管理系统 2 新建项目(project)并进行设置
  4. [bzoj1391]order
  5. 测试平台系列(80) 封装Redis客户端
  6. ML2021 | (腾讯)PatrickStar:通过基于块的内存管理实现预训练模型的并行训练
  7. java8特性表达式
  8. Python实战:截图识别文字,过万使用量版本!(附源码!!)
  9. CF1511E Colorings and Dominoes
  10. Codeforces 566E - Restoring Map(bitset 优化构造)