行锁、表锁、间隙锁

行锁、表锁、间隙锁

InnoDB 行锁锁索引

精确打击的高并发锁

作用:只锁定被修改的索引记录,允许多个事务同时修改同一张表的不同行,大幅提升并发能力。

实现原理(关键)

行锁并非直接锁“物理行”,而是加在索引记录上记录锁(Record Lock)。其工作机制是:

1. InnoDB 通过聚簇索引找到要修改的叶子节点记录。

2. 在该记录的索引项上添加 X锁(排他锁)S锁(共享锁)

3. 若无可用索引,MySQL 会进行全表扫描,导致行锁升级为表锁,即锁定所有记录。

验证方法:执行 SELECT * FROM performance_schema.data_locks; 可看到具体锁住了哪个索引页的哪条记录。

表锁:全表锁定

最粗粒度的强制排他

作用:确保在修改表结构(DDL)、全表备份或执行 LOCK TABLES 时,没有其他事务在进行读写,避免数据不一致。

实现原理

  • 元数据锁(MDL):InnoDB 中最常见的“表级锁”。当你执行 ALTER TABLEDROP TABLESELECT ... FOR UPDATE 时,MySQL 会自动加 MDL 读锁或写锁。MDL 锁不依赖存储引擎,在 Server 层管理。
  • 显式表锁:通过 LOCK TABLES table_name WRITE; 直接锁定整张表,释放前其他会话无法读写。

特点:并发度极低,生产中应避免长事务持有 MDL 锁导致业务阻塞。

间隙锁(gap lock)防止幻读

消灭“幻读”的隐形墙

作用:在 可重复读(RR) 隔离级别下,阻止其他事务在某个范围内插入新记录,从而防止两次读取结果集不一致的幻读现象。

实现原理

间隙锁是一种纯粹的“阻塞插入”锁,它锁定的是索引记录之间的空隙,而非记录本身。多个事务可以在同一间隙上持有冲突的间隙锁,因为它们的目标都是阻止插入,而不是修改现有数据。

经典案例:表 tid 列有索引,现有数据为 5, 10, 15

  • 事务 A 执行 SELECT * FROM t WHERE id > 8 AND id < 12 FOR UPDATE;
  • 此时 InnoDB 会在 id5-1010-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 的默认行锁形态,锁定区间为左开右闭

posted @ 2026-05-07 16:57  xzlrf  阅读(3)  评论(0)    收藏  举报