MySQL可重复读隔离级别下,为什么普通索引范围查询临键锁不退化为间隙锁?

新手上路,请多包涵

MySQL可重复读隔离级别下,普通索引范围查询,小于查询或者小于等于查询,查询到第一个不符合条件的索引时,为什么临键锁不会退化为间隙锁?如果是为了防止幻影数据,可以给出一个例子吗?目前我认为将第一个不满足查询条件的索引可以将临键锁退化为间隙锁,仍然可以防止幻影数据。请各位大佬答疑,感谢!

数据准备

表结构如下:

CREATE TABLE `student` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `age` int DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `student__index_age` (`age`)
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

表中数据SQL及图片

INSERT INTO student (id, name, age) VALUES (1, '路飞', 19);
INSERT INTO student (id, name, age) VALUES (5, '索隆', 21);
INSERT INTO student (id, name, age) VALUES (10, '山治', 22);
INSERT INTO student (id, name, age) VALUES (15, '乌索普', 20);
INSERT INTO student (id, name, age) VALUES (20, '香克斯', 39);

image.png

执行SQL

普通索引范围查询,第一个不符合条件索引,仍然被加临键锁,没有退化为间隙锁
begin;
select * from student where age < 25 for update;
select * from performance_schema.data_locks;
commit;
加锁数据:
二级索引age加锁区间:(-无穷,19],(19,20],(20,21],(21,22],(22,39]
image.png

尝试:普通索引范围查询小于,小于等于条件都尝试过,没有出现第一个不符合条件的索引的临键锁退化为间隙锁的现象。
希望获得结果:为什么不发生退化的原因?

阅读 400
1 个回答

在MySQL的可重复读(RR)隔离级别下,普通索引范围查询中不发生临键锁退化为间隙锁的根本原因是为了完全防止幻读现象

假设事务A执行:

BEGIN;
SELECT * FROM student WHERE age < 25 FOR UPDATE;
-- 此时不仅会锁住age为19, 20, 21, 22的记录,还会锁住age=39的记录

如果临键锁在遇到第一个不满足条件的记录(age=39)时退化为间隙锁,那么只会锁住(22,39)这个间隙,而不会锁住age=39这个记录本身。

现在考虑如果事务B执行:

BEGIN;
UPDATE student SET age = 24 WHERE age = 39;

如果事务A只有间隙锁而没有临键锁,那么事务B可以成功修改age=39的记录,使其变为age=24。这就导致了幻读问题,因为如果事务A再次执行最初的查询,会发现多了一条记录(原来的age=39现在变成了age=24)。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题