mysql在repeatable read隔离级别下,update语句命中普通索引时默认加next-key lock,即record lock(锁匹配行)+ gap lock(锁前一记录到当前值的间隙,如(1,3]),旨在防止幻读;若无匹配记录则仅加gap lock(如(3,5)),锁对象始终是索引项而非数据行本身。

UPDATE用普通索引时加的是Next-Key Lock
MySQL在REPEATABLE READ隔离级别下,UPDATE语句命中普通索引(非唯一)时,默认加的是Next-Key Lock——也就是Record Lock + Gap Lock的组合。它不只锁住匹配到的行,还会锁住该索引值前一个记录到当前记录之间的间隙。
比如表里有id字段是普通索引,数据为1, 3, 5, 8,执行:UPDATE t SET name = 'x' WHERE id = 3;
实际加锁范围是(1, 3]:即id = 3这行被Record Lock锁定,同时(1, 3)这个间隙被Gap Lock封锁,防止其他事务插入id = 2。
- 这个行为是为了防止幻读,不是“多此一举”,而是InnoDB在RR级别下的默认保护机制
- 即使你只更新一行,只要
WHERE走的是普通索引,就大概率触发Next-Key Lock - 锁对象始终是索引项,不是数据行本身;主键索引上也会对应加
Record Lock
普通索引没查到数据时加Gap Lock
当UPDATE的WHERE条件走普通索引但没匹配到任何记录,InnoDB不会什么都不锁,而是对“应该存在但实际缺失”的位置加Gap Lock。
还是上面那组数据1, 3, 5, 8,执行:UPDATE t SET name = 'y' WHERE id = 4;
因为id = 4不存在,而它落在(3, 5)之间,InnoDB就会在普通索引上对(3, 5)加Gap Lock——禁止其他事务插入id = 4或id = 4.5(如果允许浮点)这类值。
- 这种锁不可见于
INFORMATION_SCHEMA.INNODB_TRX,只能靠SHOW ENGINE INNODB STATUS\G里找lock_mode X locks gap before rec确认 - 它不阻塞
SELECT,但会阻塞在该间隙内INSERT或UPDATE(如果更新后落入该间隙) - 如果普通索引区分度极低(比如只有两个值),优化器可能干脆放弃走索引,退化为全表扫描+全表
Next-Key Lock
为什么普通索引比唯一索引锁得更宽?
根本原因在于索引结构:唯一索引能精确定位一条记录,普通索引允许重复值,InnoDB无法仅靠索引值判断“是否还有别的同值记录”,所以必须向前后延伸锁定范围。
举个典型例子:ALTER TABLE t ADD INDEX idx_status (status);
假设status只有'active'和'inactive'两个值,且各占一半数据:
- 执行
UPDATE t SET x = 1 WHERE status = 'active';,很可能锁住所有status = 'active'的行 + 它们之间的全部间隙 - 极端情况下,优化器认为全表扫描成本更低,直接走主键索引扫描,结果是对整张表加
Next-Key Lock - 即便加了索引,也要看
EXPLAIN输出的type是不是ref或eq_ref;如果是index或ALL,就说明没真正用上索引的过滤能力
怎么验证普通索引UPDATE到底锁了什么?
不能只看SQL写法,得看实际执行计划和锁状态。最可靠的方式是开两个会话,配合SHOW ENGINE INNODB STATUS\G观察。
步骤示例:
会话A:START TRANSACTION; UPDATE t SET v = 1 WHERE c = 10;(c是普通索引)
会话B:INSERT INTO t (c, v) VALUES (9, 'x'); —— 如果被阻塞,说明(?, 10)间隙被锁
然后在会话A中执行:SHOW ENGINE INNODB STATUS\G,搜索LOCK WAIT或Trx has been waiting段落,重点关注lock_mode和lock_trx_id。
-
lock_mode X locks rec but not gap→ 纯记录锁(少见,通常发生在唯一索引等值查询) -
lock_mode X locks gap before rec→ 间隙锁 -
lock_mode X(无额外说明)→ 默认是Next-Key Lock - 锁信息里出现
supremum pseudo-record,说明锁到了索引末尾间隙
真正麻烦的不是锁本身,而是普通索引的“模糊性”让锁范围难以预测——同一句SQL,在不同数据分布下,锁的行数和间隙可能差几倍。上线前务必用真实数据量+压测验证。











