插入意向锁是InnoDB在INSERT前加的特殊间隙锁,不阻塞其他插入意向锁,仅与间隙锁或临键锁冲突,旨在减少并发插入锁冲突、提升吞吐量,但不当使用易引发死锁或锁等待。

插入意向锁(Insert Intention Lock)是 InnoDB 在执行 INSERT 语句前加的一种特殊间隙锁(Gap Lock),它本身不阻塞其他插入意向锁,只在目标间隙已被其他事务加了互斥的间隙锁或临键锁(Next-Key Lock)时才会等待。它的核心作用是**减少并发插入时的锁冲突,提升写入吞吐量**,但用不好反而会引发死锁或锁等待。
插入意向锁怎么工作的?
当事务尝试向某个索引间隙(比如 id=5 和 id=10 之间)插入新记录时,InnoDB 不直接加传统间隙锁,而是加一个“插入意向锁”,标记“我打算往这儿插”。这个锁和别的插入意向锁兼容——多个事务可以同时在同一个间隙加插入意向锁,互不阻塞。
但它与以下锁冲突:
- 另一个事务在相同间隙上持有间隙锁(如 SELECT ... FOR UPDATE 范围扫描未命中任何行)
- 另一个事务在相同间隙上持有临键锁(如 UPDATE/DELETE 匹配到间隙边界)
例如:事务 A 执行 SELECT * FROM t WHERE id BETWEEN 5 AND 10 FOR UPDATE(没查到数据),会在 (5,10) 加间隙锁;此时事务 B 插入 id=7 就会被阻塞,因为其插入意向锁与该间隙锁冲突。
什么情况下插入意向锁会变成性能瓶颈?
常见于高并发写入、主键/唯一键分布密集、且存在范围锁操作的场景:
我愿意把本文归入我的“编程糗事”系列。尽管在正规大学课程中,接触到软件工程、企业级软件架构和数据库设计,但我还是时不时地体会到下述事实带给我的“罪恶”感,当然,都是我的主观感受,并且面向Eclipse: 你是PHP菜鸟,如果你: 1. 不会利用如phpDoc这样的工具来恰当地注释你的代码 2. 对优秀的集成开发环境如Zend Studio或Eclipse PDT视而不见 3
- 批量导入时混用 SELECT FOR UPDATE:比如先查是否存在再插入(“检查后插入”逻辑),若查用范围条件加了间隙锁,后续插入就会排队
- 自增主键 + 高频 INSERT … ON DUPLICATE KEY UPDATE:冲突更新可能触发临键锁,影响相邻插入
- 非唯一索引上的并发插入:若插入值落在同一索引间隙,又恰有其他事务对该间隙加锁,就会卡住
怎么优化并发插入?
关键思路是:**避免让插入意向锁撞上不必要的间隙锁,同时减少锁粒度和持有时间**。
- 用 INSERT IGNORE 或 ON DUPLICATE KEY UPDATE 替代“先查后插”:绕过显式加锁查询,减少间隙锁引入机会
- 确保插入字段有合适索引:特别是唯一约束字段,让 InnoDB 快速定位冲突位置,避免全表/大范围间隙扫描
- 控制事务粒度:插入操作尽量单事务完成,避免长事务持锁期间其他写入被阻塞
-
必要时关闭 gap lock(谨慎!):设置
transaction_isolation = 'READ-COMMITTED'可禁用间隙锁(除外键和唯一检查外),大幅降低插入意向锁冲突概率,但需确认业务能接受幻读
如何观察插入意向锁是否在起作用?
通过 InnoDB 状态诊断:
- 查
SELECT * FROM information_schema.INNODB_TRX看当前运行事务 - 查
SELECT * FROM information_schema.INNODB_LOCK_WAITS看锁等待链 - 查
SELECT * FROM information_schema.INNODB_LOCKS(MySQL 5.7+ 已废弃,8.0 用performance_schema.data_locks)识别锁类型,插入意向锁显示为INSERT_INTENTION
配合慢日志和应用层埋点,可定位具体哪条 INSERT 被阻塞、等在哪个间隙。









