插入意向锁和间隙锁
插入意向锁(Insert Intention Lock)与间隙锁(Gap Lock)的区别
在 InnoDB 中,插入意向锁和间隙锁都是用于管理索引间隙(Gap)的锁机制,但它们的 设计目的、加锁行为 和 兼容性规则 有显著不同。以下是两者的核心区别:
- 定义与设计目标
锁类型 | 定义 | 设计目标 |
---|---|---|
间隙锁(Gap Lock) | 锁定索引记录之间的间隙(例如 (10, 20)),禁止其他事务在间隙内插入新数据。 | 防止幻读(Phantom Read),确保事务期间范围内数据的稳定性。 |
插入意向锁(Insert Intention Lock) | 一种特殊的间隙锁,表示事务 计划在某个间隙插入数据,但尚未实际插入。 | 提高并发插入效率,允许不同事务在同一个间隙内插入 不冲突 的数据。 |
- 加锁时机与行为
锁类型 | 触发操作 | 加锁范围 | 锁行为 |
---|---|---|---|
间隙锁 | 范围查询(如 SELECT ... WHERE id > 10) | 锁定一个索引间隙(如 (10, 20)) | 完全阻塞其他事务在间隙内插入数据。 |
插入意向锁 | 插入操作(如 INSERT INTO t VALUES (15)) | 锁定目标插入位置的间隙(如 (10, 20)) | 仅声明插入意图,不阻塞其他事务插入不同位置的记录(允许插入不冲突的值)。 |
- 兼容性规则
当前锁类型 | 请求的锁类型 | 是否兼容 |
---|---|---|
间隙锁 | 插入意向锁 | ❌ 不兼容(插入意向锁需等待间隙锁释放) |
插入意向锁 | 插入意向锁 | ✅ 兼容(允许不同事务在同一间隙插入不同数据,前提是插入的值不冲突) |
间隙锁 | 间隙锁 | ✅ 兼容(所有间隙锁本质是共享的) |
- 示例场景
场景 1:间隙锁阻塞插入
-- 事务 A
BEGIN;
SELECT * FROM t WHERE id > 10 AND id < 20 FOR UPDATE; -- 加间隙锁 (10, 20)
-- 事务 B
INSERT INTO t (id) VALUES (15); -- 阻塞!需等待事务 A 释放间隙锁
场景 2:插入意向锁允许并发插入
-- 事务 A
BEGIN;
INSERT INTO t (id) VALUES (15); -- 对间隙 (10, 20) 加插入意向锁
-- 事务 B
BEGIN;
INSERT INTO t (id) VALUES (18); -- 对同一间隙 (10, 20) 加插入意向锁,允许执行!
-- 两个插入操作不冲突,事务 B 不会被阻塞。
场景 3:间隙锁与插入意向锁的冲突
-- 事务 A
BEGIN;
SELECT * FROM t WHERE id > 10 FOR UPDATE; -- 加间隙锁 (10, +∞)
-- 事务 B
BEGIN;
INSERT INTO t (id) VALUES (15); -- 尝试对间隙 (10, +∞) 加插入意向锁,阻塞!
-- 事务 B 需等待事务 A 释放间隙锁。
- 死锁风险
当多个事务同时持有间隙锁并尝试插入冲突的插入意向锁时,可能引发死锁:
-- 事务 A
BEGIN;
SELECT * FROM t WHERE id = 10 FOR UPDATE; -- 加间隙锁 (prev, 10]
-- 事务 B
BEGIN;
SELECT * FROM t WHERE id = 20 FOR UPDATE; -- 加间隙锁 (prev, 20]
INSERT INTO t (id) VALUES (15); -- 尝试插入到事务 A 的间隙,被阻塞
-- 事务 A
INSERT INTO t (id) VALUES (25); -- 尝试插入到事务 B 的间隙,死锁触发!
-- InnoDB 会自动回滚一个事务。
插入意向锁也是一种行级锁
行级锁的粒度分类
行级锁的粒度包括以下类型:
- 记录锁(Record Lock):
锁定单个索引记录(例如 id=10 的行)。 - 间隙锁(Gap Lock):
锁定索引记录之间的间隙(例如 (10, 20))。 - Next-Key Lock:
记录锁 + 间隙锁,锁定一个左开右闭区间(例如 (10, 20])。 - 插入意向锁(Insert Intention Lock):
一种特殊的间隙锁,表示事务计划在某个间隙插入数据。
插入意向锁的核心特点
锁定对象:索引间隙(例如 (10, 20)),而非具体行。
设计目标:
- 提高并发插入效率,允许多个事务在 同一间隙 插入 不冲突的数据。
兼容性: - 与普通间隙锁冲突(需等待间隙锁释放)。
- 与其他插入意向锁兼容(允许插入不同值的记录)。
插入意向锁与间隙锁的关系
1.定义与锁定范围
- 间隙锁(Gap Lock):
锁定索引记录之间的 区间(例如 (10, 20)),禁止其他事务在此区间内插入新数据。 - 插入意向锁(Insert Intention Lock):
一种特殊的间隙锁,表示事务 计划在某个区间内插入数据,锁定的是 区间内的一个插入点。例如,插入 id=15 时,会声明对区间 (10, 20) 的插入意图,但实际锁定的位置是 15 所在的点。
2.核心区别
维度 | 间隙锁 | 插入意向锁 |
---|---|---|
锁定目标 | 区间(如 (10, 20)) | 区间内的一个插入点(如 15) |
设计目的 | 防止其他事务插入数据 | 声明插入意图,提高并发插入效率 |
兼容性 | 与插入意向锁互斥 | 与其他插入意向锁兼容(不冲突插入) |
加锁行为 | 完全禁止区间内的插入操作 | 允许不冲突的插入,仅阻塞冲突操作 |
3.互斥性与阻塞机制
- 互斥性:
如果一个事务持有某个区间的 间隙锁,其他事务无法在同一区间内获取 插入意向锁。例如:-- 事务 A SELECT * FROM t WHERE id > 10 FOR UPDATE; -- 加间隙锁 (10, +∞) -- 事务 B INSERT INTO t (id) VALUES (15); -- 尝试加插入意向锁,阻塞!
- 非互斥场景:
如果插入意向锁的目标点不在其他事务的间隙锁区间内,则不会被阻塞。例如:-- 事务 A SELECT * FROM t WHERE id BETWEEN 10 AND 20 FOR UPDATE; -- 间隙锁 (10, 20) -- 事务 B INSERT INTO t (id) VALUES (25); -- 插入意向锁 (20, +∞),允许执行!
4.插入意向锁的生成时机
- 检查间隙锁:
执行插入操作时,InnoDB 会检查待插入记录的 下一条记录 是否已被加间隙锁。
例如插入 id=15,需检查 id=20(当前索引中大于 15 的最小值)是否被加锁。 - 生成与等待:
如果下一条记录被加了间隙锁,则生成一个 插入意向锁,并进入等待状态。
若锁状态最终为 正常(GRANTED),表示插入成功;否则事务被阻塞,直到间隙锁释放。
5.本质关系
- 从属关系:
插入意向锁是 间隙锁的子类型,但它仅表示插入意图,而非完全禁止插入。 - 锁升级:
当插入操作因间隙锁被阻塞时,插入意向锁会与间隙锁形成互斥,直到间隙锁释放后,插入意向锁才能生效。