唯一索引等值查询且记录存在时退化为记录锁:使用主键或唯一二级索引进行=或IN单值查询且值真实存在时,Next-Key Lock退化为纯Record Lock,仅锁定匹配行。

唯一索引等值查询且记录存在时退化为记录锁
Next-Key Lock 退化为纯 Record Lock 的最典型、最确定的场景,就是使用**唯一索引(主键或唯一二级索引)做等值查询(= 或 IN 单值),且该值在表中真实存在**。此时 InnoDB 认为仅锁定这一行就足以防止幻读——别人既不能改它、删它,也不能插入相同唯一值,所以没必要再锁左边间隙。
-
SELECT * FROM t WHERE id = 5 FOR UPDATE:id 是主键,且表中真有id = 5的行 → 只加 X 型 Record Lock 在该行 -
UPDATE t SET name='x' WHERE uk_col = 'a' AND uk_col IS NOT NULL:uk_col 是唯一索引,且值'a'存在 → 同样只锁匹配到的那一条 - 若查询条件带
OR、LIKE、!=或使用函数(如WHERE UPPER(name) = 'A'),即使走唯一索引,也可能无法精准定位,导致不退化
范围查询中部分命中时仍可能退化
唯一索引范围查询(如 WHERE id >= 10 AND id )不是全退化,而是“逐点判断”:对每个扫描到的索引项,按其是否满足等值条件、是否存在,分别决定是否退化。例如,扫描到 <code>id = 10(存在)→ 退化为 Record Lock;扫描到 id = 14(也存在)→ 同样退化;但终止点(如 id = 15 不满足 )则可能退化为 Gap Lock。
- 关键看执行计划是否真正“停在存在记录上”,而不是跳过或找不到
-
EXPLAIN FORMAT=tree可辅助确认实际访问的索引值和范围边界 - MySQL 8.0.26+ 对这类退化更激进;老版本(如 5.7)有时会保守保留 Next-Key
为什么不能只看 SQL 写法就断定退化发生
退化不是语法开关,而是运行时决策。同一句 SELECT ... WHERE uk = ? FOR UPDATE,参数传 5(存在)就退化,传 999(不存在)就变成 Gap Lock——你必须结合实际数据分布和执行路径判断。
- 查
INFORMATION_SCHEMA.INNODB_TRX和SHOW ENGINE INNODB STATUS\G中的lock_mode X locks rec but not gap才是铁证 - 用
SELECT @@tx_isolation确认当前是REPEATABLE-READ,否则 Next-Key 机制本身不启用 - 如果
uk_col上有重复值(违反唯一约束),InnoDB 会拒绝写入,但查询时仍按唯一索引逻辑加锁——前提是约束未被禁用或失效
真实环境里最容易忽略的是:你以为走了唯一索引,其实优化器选了全表扫描(比如隐式类型转换、字符集不匹配),结果整张表被 Next-Key 锁住。退化只发生在“真走唯一索引 + 真命中”这个狭窄交集里。


















