表里一条数据都没有, 为什么 INSERT 全部被阻塞? 而 DELETE 却畅通无阻? —— 今天带你彻底搞懂 InnoDB Gap 锁的“宇宙级”封锁逻辑。
当前是这样一个场景:
-- 表 t1刚创建,完全为空
CREATE TABLE t1(id INT PRIMARY KEY, name VARCHAR(10)) ENGINE=InnoDB;
-- Session A
BEGIN;
SELECT * FROM t1 WHERE id = 5 FOR UPDATE; -- 返回空
-- Session B
INSERT INTO t1 VALUES (1, 'a'); -- ❌ 卡住了!
INSERT INTO t1 VALUES (100, 'b'); -- ❌ 也卡住了!

表是空的,查询也没结果,凭什么不让插数据?更神奇的是删除毫无阻塞,瞬间返回结果:
-- Session C
DELETE FROM t1 WHERE id = 999; -- ✅ 瞬间返回,毫无阻塞!
DELETE FROM t1 WHERE id = 1; -- ✅ 瞬间返回,毫无阻塞!
DELETE FROM t1 WHERE id = 5; -- ✅ 瞬间返回,毫无阻塞!
这背后,正是InnoDB的Gap锁机制在默默运作。今天我们就以MySQL8.4为基准,彻底拆解Gap锁在不同数据状态下的行为边界。
在InnoDB中,除了常见的记录锁(Record Lock),还有两种关键锁类型:
Gap 锁(Gap Lock):锁定索引记录之间的“间隙”,防止新记录插入
Next-Key 锁:记录锁 + Gap 锁,是 RR 隔离级别的默认加锁方式
⚠️ Gap 锁只在 可重复读(Repeatable Read) 下生效,在READ COMMITTED 中基本被禁用。
它的核心使命:防止幻读(Phantom Read)。
🔍 锁范围:(-∞, +∞)
当表为空时,InnoDB 的 B+ 树中只有两个伪记录:
Infimum(负无穷)
Supremum(正无穷)
执行:
SELECT * FROM t1 WHERE id = 5 FOR UPDATE;由于没有真实记录,InnoDB 将查询定位到Supremum,并在其上加一个 X,GAP 锁。通过 performance_schema.data_locks 可看到:

LOCK_MODE: X,GAP
LOCK_DATA: supremum pseudo-record
这意味着:整个主键空间都被 Gap 锁覆盖!影响如下:
操作 | 结果 | 原因 |
|---|---|---|
INSERT id=1 | ❌ 阻塞 | 插入意向锁与 Gap 锁冲突 |
INSERT id=100 | ❌ 阻塞 | 同上 |
DELETE WHERE id=999 | ✅ 成功 | 无匹配行,不申请任何锁 |
💡 Gap 锁只防“插入”,不防“删除不存在的行”。
假设表中已有id=10的记录:
INSERT INTO t1 VALUES (10, 'test');再执行:
SELECT * FROM t1 WHERE id = 5 FOR UPDATE;InnoDB 会在 id=10 上加 X,GAP 锁,覆盖范围:(-∞, 10)。

此时在进行插入及删除操作如下:
mysql> INSERT INTO t1 VALUES (3, 'b');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> INSERT INTO t1 VALUES (9, 'c');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> INSERT INTO t1 VALUES (11, 'd');
Query OK, 1 row affected (0.00 sec)
mysql> delete from t1 where id=10;
Query OK, 1 row affected (0.00 sec)
影响如下:
操作 | 结果 | 原因 |
|---|---|---|
INSERT id=3 | ❌ 阻塞 | 插入意向锁与 Gap 锁冲突 |
INSERT id=9 | ❌ 阻塞 | 插入意向锁与 Gap 锁冲突 |
INSERT id=11 | ✅ 成功 | 成功,不在锁范围内 |
DELETE id=10 | ✅ 成功 | 除非被其他事务锁住记录本身 |
如果当前表里有id=4 及id=10的记录时,再来看一下是什么现象。
INSERT INTO t1 VALUES (4, 'tt');
mysql> select * from t1;
+----+------+
| id | name |
+----+------+
| 4 | tt |
| 10 | test |
+----+------+
2 rows in set (0.00 sec)执行 WHERE id=5 FOR UPDATE 后,InnoDB 定位到 id=10,但知道前一个记录是 id=4,于是只锁住两者之间的间隙,Gap 范围:(4, 10)

再做相关插入及删除操作
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t1 VALUES (3, 'b');
Query OK, 1 row affected (0.00 sec)
mysql> select * from t1;
+----+------+
| id | name |
+----+------+
| 3 | b |
| 4 | tt |
| 10 | test |
+----+------+
3 rows in set (0.00 sec)
mysql> INSERT INTO t1 VALUES (5, 'c');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> INSERT INTO t1 VALUES (9, 'c');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> INSERT INTO t1 VALUES (11, 'c');
Query OK, 1 row affected (0.00 sec)
mysql> delete from t1 where id=4;
Query OK, 1 row affected (0.00 sec)
mysql> delete from t1 where id=10;
Query OK, 1 row affected (0.00 sec)
mysql> 

影响如下:
操作 | 结果 | 原因 |
|---|---|---|
INSERT id=3 | ✅ 成功(≤4) | 不在(4,10)范围内 |
INSERT id=5~9 | ❌ 阻塞 | 锁的范围是(4,10) |
INSERT id=11 | ✅ 成功 | 不在(4,10)范围内 |
DELETE id=4或 id=10 | ✅ 成功 | Gap锁不锁已有记录 |
这是很多人困惑的点,根本原因在于:
DELETE 只操作“已存在的记录”,而 Gap 锁只保护“不存在的间隙”
如果 DELETE 的条件找不到记录 → 不申请任何锁 → 立即返回
如果 DELETE 的条件命中真实记录 → 申请记录锁(X),与 Gap 锁无关
只有当该记录已被其他事务加了记录锁时,DELETE 才会阻塞 —— 但这和 Gap 锁无关
✅ 所以:Gap 锁 ≠ 万能锁,它只对 INSERT 敏感。
生产环境要尽量避免意外的全局锁,因此可以按照如下方式进行:
慎用空表的FOR UPDATE查询:尤其在初始化阶段,避免无意义的加锁
考虑使用READ COMMITTED隔离级别:在 RC 下,Gap 锁基本不生效,INSERT 并发性更高(但需接受可能的幻读)
缩短事务时间:即使加了锁,尽快提交可减少影响窗口
监控锁信息:使用 performance_schema.data_locks 实时观察锁状态:
SELECT * FROM performance_schema.data_locks;本文举例了Gap锁的三种典型范围,对应的影响小结如下:
表状态 | 查询条件 | Gap 锁范围 | 被阻塞的INSERT |
|---|---|---|---|
空表 | id=5 FOR UPDATE | (-∞, +∞) | 所有INSERT |
有 id=10 | 同上 | (-∞, 10) | id < 10 |
有 id=4,10 | 同上 | (4, 10) | 4 < id < 10 |
✅ Gap 锁越“精准”,并发性越高;数据越稀疏,锁范围越大。
Gap锁是InnoDB实现高隔离级别的“隐形守护者”,但它也是一把双刃剑——用得好,杜绝幻读;用不好,拖垮并发。
作为开发者或 DBA,理解它在空表、单记录、多记录下的不同行为,是写出高性能、高可靠 SQL 的关键一步。
下次再遇到神秘的 INSERT 阻塞,别慌——打开 data_locks,看看是不是 Gap 锁在“守护”那个你从未注意过的间隙。
关于数据库锁相关的问题,可以在留言区留言交流,也欢迎点赞、转发,给更多需要的朋友们看到此文章。