Next-Key Lock和间隙锁区别

Next-Key Lock 和间隙锁(Gap Lock)是 InnoDB 中两种不同的锁机制,它们的设计目标和使用场景有所区别,但都与 防止幻读(Phantom Read) 和 保证范围查询的一致性 密切相关。以下是它们的核心区别和具体应用场景:

  1. 定义与作用范围
锁类型 定义 锁定范围 示例(索引值为 10, 20, 30)
Next-Key Lock 记录锁(Record Lock) + 间隙锁(Gap Lock),锁定 左开右闭区间 的索引记录和间隙。 锁定一个记录及其之前的间隙 (10, 20](锁定间隙 10~20 和记录 20)
间隙锁(Gap Lock) 仅锁定 索引记录之间的间隙,不锁定记录本身。 锁定两个索引值之间的间隙 (10, 20)(仅锁定间隙,不锁定 10 或 20)
  1. 核心区别
特性 Next-Key Lock 间隙锁(Gap Lock)
锁定对象 索引记录 + 之前的间隙 仅索引记录之间的间隙
是否锁定记录 是(记录锁部分)
解决的核心问题 防止幻读和不可重复读 仅防止幻读(禁止在间隙插入新数据)
触发场景 范围查询、非唯一索引的精确查询 范围查询未命中记录、唯一索引未命中时
锁冲突 与插入、更新、删除操作均可能冲突 仅与插入操作冲突
兼容性 与其他事务的间隙锁和 Next-Key Lock 不兼容 允许其他事务对同一间隙加间隙锁
  1. Next-Key Lock 的典型场景

范围查询:

SELECT * FROM t WHERE id > 10 AND id < 20 FOR UPDATE;

锁定区间 (10, 20],阻止其他事务插入 id=15 或修改 id=20 的记录。
非唯一索引的精确查询:

SELECT * FROM t WHERE name = 'Alice' FOR UPDATE;  -- name 是普通索引

锁定 name='Alice' 的索引记录及其之前的间隙(如 (prev, 'Alice']),防止其他事务插入新的 name='Alice' 记录。

  1. 间隙锁(Gap Lock)的典型场景
    范围查询未命中记录:
SELECT * FROM t WHERE id = 25 FOR UPDATE;  -- id=25 不存在

锁定 (20, 30) 的间隙,阻止插入 id=25。
唯一索引未命中时:

SELECT * FROM t WHERE id = 25 FOR UPDATE;  -- id 是唯一索引且不存在

锁定 (20, 30) 的间隙,但不锁定任何记录(唯一索引未命中时退化为间隙锁)。

锁的行为示例
场景 1:Next-Key Lock 的锁定范围

-- 表 t 的索引值: 10, 20, 30
-- 事务 A
BEGIN;
SELECT * FROM t WHERE id = 20 FOR UPDATE;  -- 锁定 (10, 20]

-- 事务 B
INSERT INTO t (id) VALUES (15);  -- 阻塞!因为 15 在 (10, 20] 的锁定范围内
UPDATE t SET id = 25 WHERE id = 20;  -- 阻塞!记录锁部分冲突

场景 2:间隙锁的锁定范围

-- 表 t 的索引值: 10, 20, 30
-- 事务 A
BEGIN;
SELECT * FROM t WHERE id = 25 FOR UPDATE;  -- 锁定 (20, 30) 的间隙

-- 事务 B
INSERT INTO t (id) VALUES (25);  -- 阻塞!因为 25 在 (20, 30) 的间隙内
UPDATE t SET id = 25 WHERE id = 20;  -- 允许!间隙锁不锁定记录本身
  1. 锁的优化建议
    优先使用唯一索引:
    唯一索引的精确查询会退化为记录锁(如 WHERE id = 20),减少锁范围。
    避免全表扫描:
    无索引的查询可能导致全表 Next-Key Lock,严重影响并发。
    缩小查询范围:
    尽量使用精确条件(如 WHERE id IN (1,2,3))而非大范围查询(如 WHERE id > 1000)。
    控制事务粒度:
    及时提交事务,减少锁的持有时间。
posted @ 2025-02-26 14:47  lipu123  阅读(233)  评论(0)    收藏  举报