在高并发系统中,数据库的写入性能至关重要。你是否曾遇到过这样的问题:两个事务明明要插入不同的数据,却互相阻塞甚至死锁?这背后很可能就是 插入意向锁(Insert Intention Lock) 在"默默工作",我将带你理解这个常被忽视但至关重要的 InnoDB 锁机制。
一、什么时候会产生插入意向锁?
插入意向锁是在执行 INSERT 语句时,InnoDB 自动加的一种特殊间隙锁(Gap Lock)。
具体触发条件如下:
- 当前事务准备向某个 索引间隙(gap) 中插入新记录;
- 该间隙上不存在排他间隙锁(X Gap Lock);
- 数据库需要"声明"自己的插入意图,以便与其他事务协调。
✅ 举个例子:
表主键为
(10, 30),现在要插入id = 20。InnoDB 会在间隙
(10, 30)上申请一个 插入意向锁,表示:"我要在这个区间插一条数据"。
⚠️ 注意:
- 插入意向锁只在 REPEATABLE READ(RR) 隔离级别下有意义(因为只有 RR 会使用间隙锁);
- 在
READ COMMITTED级别下,由于不使用间隙锁,插入意向锁基本不会与其他锁冲突。
二、如何观察到插入意向锁?
从 MySQL 8.0 开始,我们可以通过 performance_schema.data_locks 表直接查看锁信息。
步骤示例:
sql
-- 创建测试表
CREATE TABLE t (id INT PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t VALUES (10), (30);
-- 会话 A:开启事务并执行范围查询加锁
BEGIN;
SELECT * FROM t WHERE id > 15 AND id < 25 FOR UPDATE;
-- 此时在 (10, 30) 上加了 X Gap Lock
-- 会话 B:尝试插入
BEGIN;
INSERT INTO t VALUES (20); -- 被阻塞!
-- 在会话 C 中查看锁状态
SELECT
OBJECT_NAME,
INDEX_NAME,
LOCK_TYPE,
LOCK_MODE,
LOCK_DATA
FROM performance_schema.data_locks
WHERE OBJECT_NAME = 't';
输出:

🔍 关键字段:
LOCK_MODE = 'INSERT_INTENTION'即表示插入意向锁。
对于 MySQL 5.7,可使用 SHOW ENGINE INNODB STATUS\G 查看 TRANSACTIONS 部分的锁等待信息。
三、为什么需要插入意向锁?
如果没有插入意向锁,会发生什么?
❌ 问题:多个插入操作互相阻塞
假设两个事务都要往 (10, 30) 间隙插入数据:
- 事务 A 插入 20
- 事务 B 插入 25
若没有插入意向锁,可能的解决方案是对整个间隙加排他锁,导致 即使插入位置不同,也要串行执行,严重降低并发性能。
✅ 插入意向锁的作用:
- 允许多个事务并发插入同一间隙的不同位置(如 20 和 25);
- 但阻止它们插入到已被"保护"的间隙中 (如已有
FOR UPDATE锁住该范围); - 作为"协调信号",避免不必要的阻塞。
四、插入意向锁是如何工作的?
核心机制:
- 插入意向锁是一种 特殊的间隙锁 ,模式为
INSERT_INTENTION; - 它与 其他插入意向锁兼容(多个 INSERT 可同时申请);
- 但它与 排他间隙锁(X, GAP)不兼容,会被阻塞。
工作流程:
- 事务准备插入
id=20; - InnoDB 定位到应插入的间隙(如
(10, 30)); - 尝试在该间隙上加 插入意向锁;
- 如果间隙上已有
X GAP锁(如来自SELECT ... FOR UPDATE),则等待; - 如果没有冲突,则成功加锁,并完成插入(此时还会加记录锁)。
🔄 插入完成后,插入意向锁自动释放,仅保留记录上的行锁。
兼容性矩阵(简化版):
| 锁 A \ 锁 B | 插入意向锁 | X Gap Lock |
|---|---|---|
| 插入意向锁 | ✅ 兼容 | ❌ 冲突 |
| X Gap Lock | ❌ 冲突 | ❌ 冲突 |
五、什么场景下需要插入意向锁来解决问题?
虽然插入意向锁是 InnoDB 自动管理的,但理解它能帮助我们设计更健壮的高并发系统。
避免"幻读"下的插入冲突
在 RR 隔离级别下,业务先查询某范围无数据,再插入(如生成唯一编号):
sql
SELECT COUNT(*) FROM orders WHERE order_no LIKE '202512%'; -- 返回 0
INSERT INTO orders(order_no) VALUES ('202512001');
若不用 FOR UPDATE,两个并行的事务可能都看到 count=0,导致重复插入。
→ 利用锁机制的做法:SELECT ... FOR UPDATE 锁住间隙,此时其他插入会被插入意向锁机制阻塞,保证唯一性, 避免幻读。
结语
插入意向锁虽小,却是 InnoDB 实现 高并发写入与一致性平衡 的精巧设计。它不是问题的根源,而是协调者------在保证数据安全的前提下,尽可能释放并发能力。
作为开发者,我们无需手动控制它,但必须理解其行为,才能:
- 正确使用
FOR UPDATE避免业务漏洞; - 快速诊断死锁;
- 合理选择隔离级别以优化性能。
下次当你看到"insert intention waiting"时,就知道:这不是 bug,而是数据库在努力为你协调并发!
💡 感谢你看完这篇内容,这是我自己在工作学习中遇到的case,做一些简单的研究,并总结经验,如有遗漏或不合理的地方,欢迎你提出问题,让我们一起探索。