Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > mysql间隙锁与临键锁

MySQL间隙锁与临键锁测试(最新推荐)

作者:一本正经的不务正业

间隙锁和临键锁共同确保了事务的隔离性和一致性,防止了幻读(Phantom Reads)的发生,这篇文章给大家介绍了MySQL间隙锁与临键锁测试,感兴趣的朋友一起看看吧

在MySQL数据库中,间隙锁(Gap Locks)和临键锁(Next-Key Locks)是实现行级锁的一种方式,它们主要用于支持事务的隔离级别,尤其是在可重复读(REPEATABLE READ)和串行化(SERIALIZABLE)隔离级别下。间隙锁和临键锁共同确保了事务的隔离性和一致性,防止了幻读(Phantom Reads)的发生。

间隙锁(Gap Locks)

间隙锁锁定的是一个范围,但不包括记录本身。例如,如果事务A在范围(10, 20)上加了间隙锁,那么其他事务就不能在这个范围内插入新的记录。

临键锁(Next-Key Locks)

临键锁是间隙锁和记录锁的结合,锁定一个范围并包括该范围内的最高记录。在MySQL的InnoDB存储引擎中,默认情况下,所有行级锁都是临键锁。这意味着如果一个事务对某个范围内的记录加了临键锁,那么其他事务既不能在这个范围内插入新记录,也不能修改或删除这个范围内的任何现有记录。

版本:mysql-8.4.9
表结构:

CREATE TABLE `employees2` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名',
  `age` int NOT NULL DEFAULT '0' COMMENT '年龄',
  `position` varchar(20) NOT NULL DEFAULT '' COMMENT '职位',
  `hire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间',
  PRIMARY KEY (`id`),
  KEY `age_index` (`age`)
) ENGINE=InnoDB AUTO_INCREMENT=200104 DEFAULT CHARSET=utf8mb3 COMMENT='员工记录表';

表数据:

mysql> select *  from employees2;
+----+------+-----+----------+---------------------+
| id | name | age | position | hire_time           |
+----+------+-----+----------+---------------------+
|  5 | a    |   5 | dev      | 2026-08-07 08:57:09 |
| 10 | b    |  10 | test     | 2026-08-07 08:57:19 |
| 15 | c    |  15 | prod     | 2026-08-07 08:57:38 |
| 20 | d    |  20 | prod2    | 2026-08-07 08:57:52 |
+----+------+-----+----------+---------------------+
4 rows in set (0.00 sec)

索引:age_index 为普通索引 , RR 级别;

事务1: 开启事务,查询age>10 and age < 15 并加上排他锁。此时事务1会锁住10到15 区间。包括10,不包括15。[10,15)

begin;
SELECT *  from employees2 where age >10 and age <15 for UPDATE;

事务2: age = 9 时,直接提交成功,无需等待事务1完成

mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into employees2  (id,name,age,position) values (21,'a',9,'aa');
Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

事务2: age = 10 ,必须等待事务1 提交后,才能提交。

mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into employees2  (id,name,age,position) values (22,'a',10,'aa');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql>

执行结果:

正常执行:
t2 执行 insert into employees2 (id,name,age,position) values (22,‘a’,10,‘aa’); 后,t1 commit 提交后,t2获取到锁后正常提交。

结果:此范围查询间隙锁范围包括10;

事务1继续:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql>  SELECT * FROM employees2  WHERE age > 10 AND age < 15 FOR UPDATE;
Empty set (0.00 sec)
mysql>

事务2: age = 15 是正常添加,age=15 不在事务1间隙锁范围

mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql>  insert into employees2  (id,name,age,position) values (23,'a',15,'aa');
Query OK, 1 row affected (0.00 sec)
mysql> commit
    -> ;
Query OK, 0 rows affected (0.00 sec)

结果

事务2 继续: 插入age = 14 时,在事务1间隙锁范围内,无法插入。需要等待事务1 执行结束。

mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql>  insert into employees2  (id,name,age,position) values (25,'a',14,'aa');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

结果:此范围查询间隙锁范围不包括15 ;
所以:
SELECT * FROM employees2 WHERE age > 10 AND age < 15 FOR UPDATE;
age普通索引,间隙锁范围是:包括10 ,但是不包括15 ,区间为 [10,15)

场景2 : SELECT * FROM employees2 WHERE age >= 10 AND age <= 15 FOR UPDATE;

数据恢复

mysql> select *  from employees2;
+----+------+-----+----------+---------------------+
| id | name | age | position | hire_time           |
+----+------+-----+----------+---------------------+
|  5 | a    |   5 | dev      | 2026-08-07 08:57:09 |
| 10 | b    |  10 | test     | 2026-08-07 08:57:19 |
| 15 | c    |  15 | prod     | 2026-08-07 08:57:38 |
| 20 | d    |  20 | prod2    | 2026-08-07 08:57:52 |
+----+------+-----+----------+---------------------+
4 rows in set (0.00 sec)

事务1:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql>  SELECT * FROM employees2  WHERE age >= 10 AND age <= 15 FOR UPDATE;
+----+------+-----+----------+---------------------+
| id | name | age | position | hire_time           |
+----+------+-----+----------+---------------------+
| 10 | b    |  10 | test     | 2026-08-07 08:57:19 |
| 15 | c    |  15 | prod     | 2026-08-07 08:57:38 |
+----+------+-----+----------+---------------------+
2 rows in set (0.00 sec)
mysql>

事务2: 插入 age = 4 没有问题,插入age=5 时,无法插入。原因事务1 的间隙锁 包含 age=5

mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql>  insert into employees2  (id,name,age,position) values (21,'a',4,'aa');
Query OK, 1 row affected (0.00 sec)
mysql>  insert into employees2  (id,name,age,position) values (22,'a',5,'aa');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql>

正常情况:事务1 commit后,事务2立即插入age=5成功。

事务2继续: 事务2插入age=20 正常插入,插入age=19 时,无法插入。原因事务1 的间隙锁 包含 age=19**

mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql>  insert into employees2  (id,name,age,position) values (23,'a',20,'aa');
Query OK, 1 row affected (0.00 sec)
mysql>  insert into employees2  (id,name,age,position) values (24,'a',19,'aa');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql>

age >= 10 AND age <= 15 FOR UPDATE;
携带=时,普通索引 的区间 age 在 [5,20) ,包含5,不包含20.

场景3:SELECT * FROM employees2 where age BETWEEN 10 and 15 FOR UPDATE;;
与 age >= 10 AND age <= 15 FOR UPDATE; 一致, 事务1间隙锁区间在 [5,20)

使用主键索引:

SQL:SELECT * FROM employees2 WHERE id >= 10 AND id <= 15 FOR UPDATE;
区间范围为:[10,15]

事务1:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM employees2  WHERE id >= 10 AND id <= 15 FOR UPDATE;
+----+------+-----+----------+---------------------+
| id | name | age | position | hire_time           |
+----+------+-----+----------+---------------------+
| 10 | b    |  10 | test     | 2026-08-07 08:57:19 |
| 15 | c    |  15 | prod     | 2026-08-07 08:57:38 |
+----+------+-----+----------+---------------------+
2 rows in set (0.00 sec)
mysql>

事务2:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into employees2  (id,name,age,position) values (9,'a',5,'aa');
Query OK, 1 row affected (0.00 sec)
mysql>  insert into employees2  (id,name,age,position) values (16,'a',5,'aa');
Query OK, 1 row affected (0.00 sec)
mysql>  update employees2 set name  = 'bb' where id = 10;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> update employees2 set name  = 'bb' where id = 15;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql>

SQL: SELECT * FROM employees2 WHERE id > 10 AND id < 15 FOR UPDATE;
事务1间隙锁区间范围为:(10,15)
事务1:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql>  SELECT * FROM employees2  WHERE id > 10 AND id < 15 FOR UPDATE;
Empty set (0.00 sec)
mysql>

事务2:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql>
mysql> update employees2 set name  = 'bb' where id = 10;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
mysql> update employees2 set name  = 'bb' where id = 15;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
mysql>  insert into employees2  (id,name,age,position) values (11,'a',1,'aa');  
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql>  insert into employees2  (id,name,age,position) values (14,'a',1,'aa');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql>

SQL:SELECT * FROM employees2 where id BETWEEN 10 and 15 for update;
事务1 间隙锁区间范围为:[10,15]
事务1:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM employees2 where id BETWEEN   10 and 15 for update;
+----+------+-----+----------+---------------------+
| id | name | age | position | hire_time           |
+----+------+-----+----------+---------------------+
| 10 | b    |  10 | test     | 2026-08-07 08:57:19 |
| 15 | c    |  15 | prod     | 2026-08-07 08:57:38 |
+----+------+-----+----------+---------------------+
2 rows in set (0.00 sec)
mysql>

事务2:

mysql> begin
    -> ;
Query OK, 0 rows affected (0.00 sec)
mysql>  insert into employees2  (id,name,age,position) values (9,'a',5,'aa');
Query OK, 1 row affected (0.00 sec)
mysql> insert into employees2  (id,name,age,position) values (16,'a',5,'aa');
Query OK, 1 row affected (0.00 sec)
mysql>  update employees2 set name  = 'bb' where id = 10;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql>  update employees2 set name  = 'bb' where id = 15;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql>

到此这篇关于MySQL间隙锁与临键锁测试(最新推荐)的文章就介绍到这了,更多相关mysql间隙锁与临键锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文