MySQL记录锁、间隙锁、临键锁案例测试

文章目录

    • 一、数据和环境准备
      • 1、创建表和数据
    • 二、主键索引或唯一索引示例
      • 1、等值查询且数据存在示例
      • 2、等值查询且数据不存在示例
      • 3、范围查询示例
    • 三、普通索引示例
      • 1、等值查询值
      • 2、左开右闭原则
    • 四、无索引示例
      • 1、等值查询值
      • 2、等值查询且数据不存在示例
      • 3、范围查询
    • 五、总结
      • 1.主键索引/唯一索引
      • 2.普通索引
      • 3.无索引

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

参考:https://www.jianshu.com/p/95d71b150782
参考:https://blog.csdn.net/weixin_39035120/article/details/105937400

记录锁:对表中的记录加锁,叫做记录锁,简称行锁。

  • InnoDB 中的行锁的实现依赖于索引,一旦某个加锁操作没有使用到索引,那么该锁就会退化为表锁。
  • 记录锁存在于包括主键索引在内的唯一索引中,锁定单条索引记录。

间隙锁:当我们使用索引无论是等值还是范围查询,没有命中一条记录时候,加的就是间隙锁。

临键锁:当我们使用索引进行范围查询,命中了记录的情况下,就是使用了临键锁,他相当于记录锁+间隙锁。

  • 唯一性索引,等值查询匹配到一条记录的时候,退化成记录锁。
  • 没有匹配到任何记录的时候,退化成间隙锁。

一、数据和环境准备

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;


insert into t values(1,1,123,"张三");
insert into t values(4,4,456,"李四");
insert into t values(7,7,789,"王五");

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

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

MySQL记录锁、间隙锁、临键锁案例测试_第1张图片

二、主键索引或唯一索引示例

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

MySQL记录锁、间隙锁、临键锁案例测试_第2张图片
事务A 等值查询id=4,因为id是主键,同时是等值查询存在该记录,所以只会在id=4这条记录上加记录锁(行锁),不会加间隙锁。

事务B 等值查询id=5,没有锁冲突,所以查询正常,不会堵塞。(如果事务B 等值查询id=4,加读锁可以,写锁阻塞)

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

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

  • 正常应该生成(5,7]的临键锁,但是由于这是主键索引或唯一主键索引 ,会退化成(5,7)的间隙锁

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

3、范围查询示例

MySQL记录锁、间隙锁、临键锁案例测试_第3张图片
事务A 范围查询id>3,那么这里就会存在一个(1,4],(4,7],(7,+∞]的临键(next-key)锁。

  • 临键锁退化为间隙锁

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

三、普通索引示例

1、等值查询值

MySQL记录锁、间隙锁、临键锁案例测试_第4张图片

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

事务B 插入的age在(1,4]和(4,7]都被阻塞

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能插入。

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

四、无索引示例

1、等值查询值

在这里插入图片描述
事务A 等值查询 mobile = 123,因为mobile是无索引的,所以这个for update,变成表级排他(X)锁。

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

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

在这里插入图片描述
事务A 等值查询 mobile = 222未命中数据,因为mobile是无索引的,所以这个for update,变成表级排他(X)锁。

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

3、范围查询

在这里插入图片描述
事务A 范围查询 mobile > 222,不管命没命中数据,因为mobile是无索引的,所以这个for update,变成表级排他(X)锁。

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

五、总结

1.主键索引/唯一索引

等值命中:加行锁,其他事务可加读锁,不可加写锁
等值未命中:加间隙锁
范围查询不管是否命中数据都加间隙锁

2.普通索引

等值命中:加临键锁
等值未命中:加间隙锁
范围查询:

  • 命中数据加临键锁
  • 未命中加间隙锁

3.无索引

无索引字段不管等值查询命中,等值查询未命中,范围查询都会加表级锁

你可能感兴趣的:(mysql,mysql,java,记录锁,间隙锁,临键锁)