Mysql锁之插入意向锁

在高并发系统中,数据库的写入性能至关重要。你是否曾遇到过这样的问题:两个事务明明要插入不同的数据,却互相阻塞甚至死锁?这背后很可能就是 插入意向锁(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

若没有插入意向锁,可能的解决方案是对整个间隙加排他锁,导致 即使插入位置不同,也要串行执行,严重降低并发性能。

✅ 插入意向锁的作用:

  1. 允许多个事务并发插入同一间隙的不同位置(如 20 和 25);
  2. 但阻止它们插入到已被"保护"的间隙中 (如已有 FOR UPDATE 锁住该范围);
  3. 作为"协调信号",避免不必要的阻塞

四、插入意向锁是如何工作的?

核心机制:

  • 插入意向锁是一种 特殊的间隙锁 ,模式为 INSERT_INTENTION
  • 它与 其他插入意向锁兼容(多个 INSERT 可同时申请);
  • 但它与 排他间隙锁(X, GAP)不兼容,会被阻塞。

工作流程:

  1. 事务准备插入 id=20
  2. InnoDB 定位到应插入的间隙(如 (10, 30));
  3. 尝试在该间隙上加 插入意向锁
  4. 如果间隙上已有 X GAP 锁(如来自 SELECT ... FOR UPDATE),则等待;
  5. 如果没有冲突,则成功加锁,并完成插入(此时还会加记录锁)。

🔄 插入完成后,插入意向锁自动释放,仅保留记录上的行锁。

兼容性矩阵(简化版):

锁 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,做一些简单的研究,并总结经验,如有遗漏或不合理的地方,欢迎你提出问题,让我们一起探索

相关推荐
devmoon1 天前
在 Polkadot Runtime 中添加多个 Pallet 实例实战指南
java·开发语言·数据库·web3·区块链·波卡
认真的薛薛1 天前
数据库-sql语句
数据库·sql·oracle
爱学英语的程序员1 天前
面试官:你了解过哪些数据库?
java·数据库·spring boot·sql·mysql·mybatis
·云扬·1 天前
MySQL Redo Log落盘机制深度解析
数据库·mysql
用户982863025681 天前
pg内核实现细节
数据库
码界筑梦坊1 天前
330-基于Python的社交媒体舆情监控系统
python·mysql·信息可视化·数据分析·django·毕业设计·echarts
飞升不如收破烂~1 天前
Redis 分布式锁+接口幂等性使用+当下流行的限流方案「落地实操」+用户连续点击两下按钮的解决方案自用总结
数据库·redis·分布式
workflower1 天前
业务需求-假设场景
java·数据库·测试用例·集成测试·需求分析·模块测试·软件需求
亓才孓1 天前
[JDBC]基于三层架构和MVC架构的JDBCTools
数据库
IT邦德1 天前
RPM包快速安装Oracle26ai
数据库·oracle