行锁、表锁、间隙锁
行锁、表锁、间隙锁
InnoDB 行锁锁索引
精确打击的高并发锁
作用:只锁定被修改的索引记录,允许多个事务同时修改同一张表的不同行,大幅提升并发能力。
实现原理(关键):
行锁并非直接锁“物理行”,而是加在索引记录上的记录锁(Record Lock)。其工作机制是:
1. InnoDB 通过聚簇索引找到要修改的叶子节点记录。
2. 在该记录的索引项上添加 X锁(排他锁) 或 S锁(共享锁)。
3. 若无可用索引,MySQL 会进行全表扫描,导致行锁升级为表锁,即锁定所有记录。
验证方法:执行 SELECT * FROM performance_schema.data_locks; 可看到具体锁住了哪个索引页的哪条记录。
表锁:全表锁定
最粗粒度的强制排他
作用:确保在修改表结构(DDL)、全表备份或执行 LOCK TABLES 时,没有其他事务在进行读写,避免数据不一致。
实现原理:
- 元数据锁(MDL):InnoDB 中最常见的“表级锁”。当你执行
ALTER TABLE、DROP TABLE或SELECT ... FOR UPDATE时,MySQL 会自动加 MDL 读锁或写锁。MDL 锁不依赖存储引擎,在 Server 层管理。 - 显式表锁:通过
LOCK TABLES table_name WRITE;直接锁定整张表,释放前其他会话无法读写。
特点:并发度极低,生产中应避免长事务持有 MDL 锁导致业务阻塞。
间隙锁(gap lock)防止幻读
消灭“幻读”的隐形墙
作用:在 可重复读(RR) 隔离级别下,阻止其他事务在某个范围内插入新记录,从而防止两次读取结果集不一致的幻读现象。
实现原理:
间隙锁是一种纯粹的“阻塞插入”锁,它锁定的是索引记录之间的空隙,而非记录本身。多个事务可以在同一间隙上持有冲突的间隙锁,因为它们的目标都是阻止插入,而不是修改现有数据。
经典案例:表 t 的 id 列有索引,现有数据为 5, 10, 15。
- 事务 A 执行
SELECT * FROM t WHERE id > 8 AND id < 12 FOR UPDATE; - 此时 InnoDB 会在
id值5-10和10-15的间隙上加间隙锁。 - 事务 B 尝试
INSERT INTO t VALUES (11);—— 被阻塞,因为 11 落在锁定间隙内。
> 注意:间隙锁仅在 可重复读(RR) 隔离级别下生效,读已提交(RC) 级别下无间隙锁,因此 RC 级别无法完全避免幻读。
临键锁(next-key lock)
在实际的 InnoDB 锁结构中,往往是 记录锁 + 间隙锁 的组合,称为 Next-Key Lock。它是 InnoDB 在 RR 级别下防止幻读的默认行锁算法。
- 锁定区间:左开右闭(例如锁定
(5, 10],即 5 到 10 的间隙加上 10 这条记录)。 - 退化机制:
- 若查询条件是等值且命中唯一索引,退化为 记录锁。
- 若查询条件未命中记录,退化为 间隙锁。
什么 SQL 会触发间隙锁
以下情况会在 RR 级别下触发间隙锁(Gap Lock / Next-Key Lock):
1. 等值查询但未命中记录
-- 表中 id 有 10, 20, 30
-- 等值查询未命中,加间隙锁 (10, 20)
SELECT * FROM t WHERE id = 15 FOR UPDATE;
2. 范围查询
-- 范围查询,锁定范围内的间隙 + 记录
SELECT * FROM t WHERE id > 10 AND id < 30 FOR UPDATE;
-- 锁定区间:(10, 20] + (20, 30](假设中间有 20)
3. 唯一索引等值查询但未命中
-- unique_key 上有 10, 30,查 20 未命中
-- 加间隙锁 (10, 30)
SELECT * FROM t WHERE unique_key = 20 FOR UPDATE;
4. 唯一索引范围查询
-- 即使是唯一索引,范围查询也会加间隙锁
SELECT * FROM t WHERE unique_key > 10 AND unique_key < 30 FOR UPDATE;
5. 半开区间查询
-- id 有 10, 20, 30
-- 范围查询 id > 10,锁定 (10, 20] + (20, 30] + (30, +∞)
SELECT * FROM t WHERE id > 10 FOR UPDATE;
-- 注意:最后一个区间会锁到正无穷,其他事务无法插入 > 30 的数据
不会触发间隙锁的情况:
- 等值查询命中唯一索引 -> 退化为记录锁
- RC 隔离级别下 -> 没有间隙锁
- 普通 SELECT(快照读)-> 不加任何锁
间隙锁冲突规则:
| 锁类型 | 记录锁 | 间隙锁 | 插入意向锁 |
|--------|--------|--------|-----------|
| 记录锁(X) | 冲突 | 不冲突 | 冲突 |
| 间隙锁 | 不冲突 | 不冲突 | 冲突 |
| 插入意向锁 | 冲突 | 冲突 | 不冲突 |
> 间隙锁之间不冲突(因为都是阻止插入,不是修改现有数据),但间隙锁与插入意向锁冲突。
行锁:锁定一条精确的、已存在的索引记录。
间隙锁:查询范围落在索引记录之间的空隙,且不包含任何已存在的记录。间隙锁是纯粹的”防插入墙”,不影响已存在记录的修改。
临键锁:在 RR 隔离级别下,任何范围查询且命中记录的操作,几乎都会触发临键锁。它是 InnoDB 的默认行锁形态,锁定区间为左开右闭。

浙公网安备 33010602011771号