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

相关推荐
qq192572302721 小时前
商品库存管理系统(MYSQL)
数据库·mysql
什么都不会的Tristan21 小时前
Feed流(关注推送)
java·前端·数据库
wu_jing_sheng021 小时前
黑龙江省保险补贴Shapefile转换工具:GIS数据处理自动化实践
大数据·数据库·人工智能
GrowingYi21 小时前
分布式数据库事务实现
数据库·分布式·database
托尼吴1 天前
milvus 向量数据库学习笔记-基础认识
数据库·学习·milvus
徐同保1 天前
使用n8n中的HTTP Request节点清空pinecones向量数据库
数据库·网络协议·http
小北方城市网1 天前
第 9 课:Python 全栈项目性能优化实战|从「能用」到「好用」(企业级优化方案|零基础落地)
开发语言·数据库·人工智能·python·性能优化·数据库架构
ChineHe1 天前
Redis入门篇001_Redis简介与特性
数据库·redis·缓存
仓颉也为难1 天前
全表扫和索引在哪种场景哪个效率高、基线分水岭在哪
数据库