Mysql--基础知识点--98--临键锁 VS 间隙锁

张开发
2026/4/15 1:53:13 15 分钟阅读

分享文章

Mysql--基础知识点--98--临键锁 VS 间隙锁
在 MySQL InnoDB 中临键锁和间隙锁都是用于解决幻读在 RR 隔离级别下的锁机制但它们的作用范围不同间隙锁Gap Lock只锁定索引记录之间的“间隙”不锁定记录本身。临键锁Next-Key Lock是行锁 间隙锁的组合锁定一个记录及其前面的间隙左开右闭区间。主要区别维度间隙锁临键锁锁定范围仅间隙记录之间的空间记录本身 该记录前面的间隙是否锁定记录❌ 否✅ 是典型产生场景查询条件使用范围但命中不存在的记录默认的索引扫描/范围查询RR隔离级别目的防止幻读阻止其他事务在间隙中插入新记录防止幻读 保护已有记录举例说明假设有一个表tid是主键现有记录1, 3, 5。当前事务隔离级别为REPEATABLE READ1. 临键锁示例-- 事务 ABEGIN;SELECT*FROMtWHEREid3FORUPDATE;InnoDB 会对id3这条记录加上临键锁锁定的区间为(1, 3]即从上一个记录1到当前记录3的区间包含3本身。此时事务 B 尝试INSERT INTO t VALUES (2);→阻塞因为(1,3)间隙被锁事务 B 尝试INSERT INTO t VALUES (3);→阻塞记录3被锁事务 B 尝试INSERT INTO t VALUES (4);→不阻塞4属于间隙(3,5)未被锁2. 间隙锁示例-- 事务 ABEGIN;SELECT*FROMtWHEREid2FORUPDATE;-- id2 不存在因为id2不存在InnoDB 只对间隙(1, 3)加上间隙锁不会锁定任何记录记录1和3本身未被锁。此时事务 B 尝试INSERT INTO t VALUES (2);→阻塞在间隙(1,3)中插入事务 B 尝试INSERT INTO t VALUES (1);→不阻塞1是已存在记录且未被锁但若1不存在插入1也不阻塞因为间隙锁范围是(1,3)不包括1事务 B 尝试INSERT INTO t VALUES (3);→不阻塞记录3本身没有被锁事务 B 尝试UPDATE t SET id 3 WHERE id 1;→ 可以执行不影响间隙小结临键锁 行锁 间隙锁锁定范围更广用于保护现有记录及防止相邻间隙的幻读。间隙锁仅锁住“空白区域”不影响已有记录的修改或删除只阻止在间隙中插入新数据。在实际使用中InnoDB 默认采用临键锁唯一索引等值查询当命中唯一索引等值查询且记录存在时临键锁会退化为行锁当查询不存在的记录时则会退化为间隙锁。范围查询: 看下边的问题范围查询时用的是行锁、临键锁、行锁 哪个?的答案非唯一索引等值查询: 当命中非唯一索引等值查询且记录存在时还是间隙锁不会退化原因看下边的问题当命中非唯一索引等值查询且记录存在时会加什么样的锁当查询不存在的记录时则会退化为间隙锁。范围查询跟唯一索引差不多问范围查询时用的是行锁、临键锁、行锁 哪个?在 MySQL InnoDB 的RR可重复读隔离级别下范围查询默认使用的是临键锁Next-Key Lock。它本质上是“行锁 间隙锁”的组合既锁定查询范围内的已有记录行锁也锁定这些记录之间的间隙间隙锁从而防止幻读。不同情况的具体表现场景RR 隔离级别RC 隔离级别范围查询如WHERE id 10临键锁锁定索引区间及间隙行锁仅锁定命中的记录无间隙锁唯一索引等值查询记录存在行锁临键锁退化为行锁行锁唯一索引等值查询记录不存在间隙锁无锁或行锁RC 下不加间隙锁不会阻塞插入举例说明RR 级别表t(id PK)有记录1, 3, 5, 7。-- 事务 ABEGIN;SELECT*FROMtWHEREid3FORUPDATE;-- 范围查询此时 InnoDB 会加临键锁锁定的索引范围是从id3的下一条记录5开始直到正无穷。具体会锁住(3,5]、(5,7]、(7, ∞)这些区间包含记录本身和前面的间隙。影响其他事务无法在id 3的范围内插入新记录如插入4、6、8都会阻塞也无法修改或删除5、7等已有记录。这保证了在事务 A 结束前反复执行相同的范围查询结果集始终不变防止幻读。与 RC 的对比如果隔离级别是RC读已提交范围查询只加行锁仅锁住实际查到的记录5和7。不会锁间隙因此其他事务可以在间隙中插入新记录如插入4导致幻读。总结RR 隔离级别 范围查询→ 使用临键锁行锁间隙锁RC 隔离级别 范围查询→ 只使用行锁无间隙锁无临键锁所以回答你的问题范围查询在 InnoDB 中RR 下用的是临键锁。再问在RR级别那如果是 表 t(id PK) 有记录1, 3, 5, 7。id3 呢锁住的范围是什么在RR可重复读隔离级别下对id 3的范围查询使用FOR UPDATE加锁时InnoDB 会使用**临键锁Next-Key Lock**锁定以下范围锁定的具体范围表中有记录1, 3, 5, 7。WHERE id 3会命中记录3, 5, 7。1. 对每个命中记录加临键锁左开右闭区间对于id 3临键锁区间为(1, 3]对于id 5临键锁区间为(3, 5]对于id 7临键锁区间为(5, 7]2. 还需要锁定最大记录之后的间隙最后一个记录7之后的正无穷区间(7, ∞)也是一个间隙锁3. 综合结果锁定的所有区间为(1, 3]∪(3, 5]∪(5, 7]∪(7, ∞)(1, ∞)即所有大于 1 的 id 值包括记录和间隙都被锁住。记录被锁id 3, 5, 7不能由其他事务修改或删除。间隙被锁任何在(1, ∞)范围内的插入操作如插入2, 4, 6, 8, 100都会被阻塞。记录1不受影响其他事务可以正常修改或删除id 1的记录。示例验证其他事务的操作是否阻塞原因INSERT INTO t VALUES (2);✅ 阻塞落在间隙(1,3)中INSERT INTO t VALUES (4);✅ 阻塞落在间隙(3,5)中INSERT INTO t VALUES (6);✅ 阻塞落在间隙(5,7)中INSERT INTO t VALUES (8);✅ 阻塞落在间隙(7, ∞)中UPDATE t SET id... WHERE id3;✅ 阻塞记录3被临键锁锁定DELETE FROM t WHERE id5;✅ 阻塞记录5被临键锁锁定UPDATE t SET ... WHERE id1;❌ 不阻塞记录1未被锁INSERT INTO t VALUES (0);❌ 不阻塞落在(-∞,1]区间未锁定总结id 3锁定的范围是id 1的所有空间即(1, ∞)。相当于从第一个命中记录的前一个间隙开始不包括前一个记录本身一直锁到正无穷。这是 InnoDB 在 RR 级别下为了防止幻读所采取的标准行为。再问当命中非唯一索引等值查询且记录存在时会加什么样的锁在RR可重复读隔离级别下当命中非唯一索引等值查询且记录存在时例如SELECT ... WHERE name Alice FOR UPDATEname为非唯一索引InnoDB 会加临键锁Next-Key Lock而不会退化为行锁。加锁的具体规则1. 对辅助索引加临键锁对于找到的每条满足条件的辅助索引记录InnoDB 会对其加上临键锁锁定区间为(上一个不同索引值, 当前索引值]。由于非唯一索引可能存在多条相同值的记录InnoDB 会锁定所有相同值记录及其之间的间隙并且还会锁定从最后一个相同值到下一个不同值之间的间隙即右边界扩展到下一个不同值。最终效果锁住从上一个不同值到下一个不同值之间的整个区间不包含左边界和右边界记录本身但包含区间内的所有记录和间隙。2. 对聚集索引主键加行锁对于每个匹配的辅助索引记录对应的主键记录InnoDB 也会加上行锁Record Lock防止其他事务通过主键直接修改或删除这些行。举例说明假设有一个表tCREATETABLEt(idINTPRIMARYKEY,nameVARCHAR(10),INDEXidx_name(name));INSERTINTOtVALUES(1,Alice),(2,Alice),(3,Bob),(4,Charlie);RR 隔离级别下事务 A 执行BEGIN;SELECT*FROMtWHEREnameAliceFORUPDATE;加锁情况辅助索引idx_name上的值分布Alice (id1),Alice (id2),Bob (id3),Charlie (id4)上一个不同索引值无可以视为-∞当前索引值Alice下一个不同索引值BobInnoDB 会锁定(-∞, Alice]和(Alice, Bob]两个临键锁区间实际上由于Alice有多条它会锁定从-∞到Bob之间的所有间隙和记录具体是临键锁1(-∞, Alice]包含第一个Alice及其前面的间隙临键锁2(Alice, Alice]不对于第二条Alice其前一条是第一条Alice所以临键锁是(Alice, Alice]这个区间实际上只包含第二条Alice本身因为两条相同的值之间没有间隙。然后还会对Alice到Bob之间的间隙加锁即临键锁(Alice, Bob]包含Bob吗不临键锁是左开右闭Bob本身属于这个锁但Bob是下一个不同值是否会被锁实际上这个临键锁会锁住Bob记录本身但Bob不满足nameAlice所以这个锁是必要的以防止幻读重要根据 InnoDB 实现范围查询会扫描到第一个不满足条件的记录并对该记录也加临键锁。因此对于等值查询nameAliceInnoDB 会定位到第一条Alice然后继续扫描直到第一个不等于Alice的记录即Bob并对Bob也加上临键锁(Alice, Bob]。这样其他事务无法在(Alice, Bob)间隙中插入新记录如插入Alice或Alice2也无法修改或删除Bob本身实际上Bob被锁住后其他事务不能修改或删除id3这一行但这是否合理这其实是 InnoDB 为了防止幻读的保守策略因为如果允许修改Bob为Alice就会导致新Alice出现影响当前查询结果。所以确实会锁住Bob。因此最终的锁定范围是从-∞到Bob的所有间隙和记录包含所有Alice和Bob记录。这样其他事务无法插入nameAlice的新记录因为间隙被锁无法插入name值在Alice和Bob之间的记录如Alice2按字符串排序在Alice和Bob之间无法修改Bob为其他值也无法修改Alice记录无法删除Alice或Bob记录对比唯一索引等值查询记录存在唯一索引不需要锁定下一个不同值因为唯一性保证了不会有新记录插入相同值且修改其他值不影响等值查询。所以退化为行锁。总结索引类型等值查询记录存在RR级别下加的锁唯一索引含主键✅行锁退化为 Record Lock非唯一索引✅临键锁Next-Key Lock锁定一个区间包含间隙和记录因此回答你的问题非唯一索引等值查询且记录存在时加的是临键锁并且还会对对应的主键记录加行锁。

更多文章