在 MySQL 的 InnoDB 存储引擎中,GAP 锁(间隙锁) 是事务隔离级别为 可重复读(REPEATABLE READ) 时引入的一种锁机制,主要用于解决幻读(Phantom Read)问题。以下是它的详细作用及解决的问题:
1. GAP 锁的作用
- 锁定一个范围,但不包括记录本身:
GAP 锁会锁定索引记录之间的“间隙”(即两个索引值之间的区间),或者第一个索引值之前或最后一个索引值之后的区间。例如:- 如果表中有索引值
[10, 20, 30]
,那么可能的间隙区间包括:(-∞, 10)
、(10, 20)
、(20, 30)
、(30, +∞)
。
- 如果表中有索引值
- 防止其他事务在间隙中插入新数据:
当一个事务对某个范围加 GAP 锁后,其他事务无法在这个范围内插入新的索引记录(但允许修改或删除已有的记录)。
2. 解决的问题:幻读(Phantom Read)
- 幻读的定义:
在同一事务中,多次执行相同的查询,但每次返回的结果集不同(其他事务插入了新数据导致新增了满足条件的行)。 - GAP 锁如何防止幻读:
通过锁定可能被插入新数据的“间隙”,确保其他事务无法在当前事务查询的范围内插入新数据,从而保证多次查询结果的一致性。
3. GAP 锁的触发场景
- 基于范围的查询(WHERE 条件使用范围条件):
例如SELECT * FROM table WHERE id BETWEEN 10 AND 20 FOR UPDATE;
此时会锁定[10, 20]
这个区间内的所有间隙(即使某些索引值不存在)。 - 等值查询针对不存在的记录:
例如SELECT * FROM table WHERE id = 15 FOR UPDATE;
如果id=15
不存在,则会锁定15
所在的间隙(比如锁定(10,20)
),防止其他事务插入id=15
。
4. GAP 锁的兼容性
- GAP 锁之间是兼容的:多个事务可以同时对同一个间隙加 GAP 锁。
- GAP 锁和记录锁(行锁)可以组合成 Next-Key Lock(默认的行锁机制),同时锁定记录和间隙。
5. 注意事项
- 仅存在于可重复读(REPEATABLE READ)隔离级别:
在 READ COMMITTED 隔离级别下,InnoDB 不会使用 GAP 锁。 - 仅针对索引生效:
如果操作没有使用索引,InnoDB 会退化为表锁(包括间隙),但性能较差。 - 可能影响并发插入性能:
因为 GAP 锁会阻止在锁定区间内插入新数据,在高并发写入场景中可能需要优化索引或调整隔离级别。
6.示例说明
假设表 t
有索引列 id
,现有数据为 [1, 5, 10]
:
-- 事务A执行:
BEGIN;
SELECT * FROM t WHERE id BETWEEN 5 AND 10 FOR UPDATE;
此时会锁定区间 (5,10]
(Next-Key Lock),包括:
- 记录锁锁定
id=5
和id=10
; - GAP 锁锁定区间
(5,10)
。
此时如果事务B尝试插入 id=7
:
-- 事务B会被阻塞:
INSERT INTO t (id) VALUES (7);
直到事务A提交后,事务B才能继续执行。
总结
GAP 锁是 InnoDB 在可重复读隔离级别下实现幻读控制的核心机制之一。它通过锁定索引间隙来防止其他事务插入新数据,从而保证查询结果的一致性。但需注意其可能对并发写入性能的影响。
内容由零声教学AI助手提供,问题来源于学员提问