间隙锁(Gap Lock)是数据库中用于解决幻读(Phantom Read)问题的一种锁机制,主要出现在可重复读(REPEATABLE READ)隔离级别下。它锁定的不是具体的记录,而是索引记录之间的间隙,防止其他事务在该间隙插入新记录,从而保证查询结果的一致性。
核心原理
锁定间隙而非记录
- 间隙锁锁定的是索引记录之间的范围,例如:
WHERE id BETWEEN 10 AND 20; -- 锁定(10, 20)的间隙 - 即使该间隙中没有数据,其他事务也无法插入新记录。
- 间隙锁锁定的是索引记录之间的范围,例如:
解决幻读问题
- 幻读:同一事务中多次查询返回不同的结果集(因其他事务插入新记录)。
- 间隙锁通过阻止插入操作,确保两次查询的结果集一致。
间隙锁的触发条件
在InnoDB中,间隙锁主要在以下场景触发:
- 范围查询:
SELECT * FROM table WHERE id > 10 FOR UPDATE; -- 锁定id>10的间隙 - 唯一索引查询但记录不存在:
SELECT * FROM table WHERE id = 20 FOR UPDATE; -- id=20不存在时,锁定(19, 21)间隙 - 插入意向锁(Insert Intention Lock):
- 当事务插入新记录时,需先获取插入意向锁,若间隙已被锁定,则插入被阻塞。
间隙锁的优缺点
优点
- 解决幻读:确保可重复读隔离级别下的查询一致性。
- 防止并发插入:避免多个事务在同一间隙插入导致的约束冲突。
缺点
- 降低并发:锁定间隙可能导致其他事务无法插入无关记录。
- 死锁风险:多个事务循环等待间隙锁时可能产生死锁。
间隙锁与其他锁的对比
| 锁类型 | 锁定对象 | 作用 | 隔离级别要求 |
|---|---|---|---|
| 记录锁 | 具体索引记录 | 防止记录被修改 | 读已提交(RC)以上 |
| 间隙锁 | 索引间隙 | 防止间隙插入新记录 | 可重复读(RR) |
| 临键锁 | 记录+间隙 | 解决幻读和当前读问题 | 可重复读(RR) |
| 插入意向锁 | 插入操作 | 表明插入意图,互不阻塞 | 可重复读(RR) |
示例场景
假设有表users,索引包含值:10、20、30。
- 事务A执行:
SELECT * FROM users WHERE id > 15 FOR UPDATE; -- 锁定间隙(15, +∞) - 事务B尝试插入:
INSERT INTO users (id) VALUES (16); -- 被阻塞,因间隙(15, +∞)已被锁定
间隙锁的配置与优化
调整隔离级别
- 设置为读已提交(READ COMMITTED)可禁用间隙锁,但会导致幻读。
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
- 设置为读已提交(READ COMMITTED)可禁用间隙锁,但会导致幻读。
优化查询条件
- 使用唯一索引或主键,减少间隙锁的范围。
- 避免范围查询,如:
WHERE id > 10。
批量操作优化
- 按顺序插入数据,避免间隙锁竞争。
死锁案例
事务A:
SELECT * FROM users WHERE id = 20 FOR UPDATE; -- 锁定间隙(19, 21)
INSERT INTO users (id) VALUES (19); -- 等待间隙锁
事务B:
SELECT * FROM users WHERE id = 19 FOR UPDATE; -- 锁定间隙(18, 20)
INSERT INTO users (id) VALUES (20); -- 等待间隙锁
结果:两个事务循环等待,导致死锁。
总结
间隙锁是InnoDB在可重复读隔离级别下为解决幻读问题引入的重要机制,通过锁定索引间隙防止新记录插入。但它会降低并发性能并增加死锁风险,需在设计数据库时谨慎使用。优化策略包括调整隔离级别、优化查询条件和合理规划业务逻辑,以平衡数据一致性与性能。