Next-Key Lock 是什么?一句话说清
Next-Key Lock 不是新锁类型,而是
Record Lock(锁某一行) +
Gap Lock(锁两个值之间的空隙)的组合体,只在
REPEATABLE READ隔离级别下由 InnoDB 自动启用,专为堵住幻读而生。
它锁的是「左开右闭区间」,比如
(90, 102]:不锁 90 这个点,但锁 102 这行 + 所有 90~102 之间的插入可能。你没写
FOR UPDATE或
LOCK IN SHARE MODE,它就不会触发;一旦用了,影响远超直觉。
什么时候真正用上 Next-Key Lock?看查询方式
加锁不是拍脑袋全表扫,而是严格按索引查找路径走,只锁「访问到的索引项」及其对应区间。关键看三点:隔离级别、是否带锁读、查询条件类型。
SELECT ... WHERE id = 15 FOR UPDATE(主键等值查)→ 退化为纯
Record Lock,只锁 id=15 这一行
SELECT ... WHERE age = 25 FOR UPDATE(普通索引等值查,且 age=25 存在)→ 锁
(24, 25]和
(25, 26]这类相邻间隙,实际可能卡住 age=25.5 的插入(如果字段允许小数)
SELECT ... WHERE age > 30 FOR UPDATE(范围查)→ 典型 Next-Key 场景,比如当前最大 age 是 35,就可能锁
(30, 35]和
(35, +∞)
WHERE字段没索引 → InnoDB 退化为全表扫描,对每条聚簇索引记录都加 Next-Key Lock,相当于整张表被锁死,极易阻塞其他事务
为什么必须靠它防幻读?MVCC 为啥不行
MVCC 能挡脏读、不可重复读,但对
INSERT无感——它不改旧行,只加新行,快照里自然看不到“未来插入”的数据。幻读的本质是:同一事务内,两次相同范围查询,第二次多出新插入的行。
仅靠
Record Lock:能锁住已存在的 id=102,但拦不住别人插 id=101;
仅靠
Gap Lock:能锁
(90, 102)防插入,但若已有 id=101,它又不锁这行本身,别人还能改它;
Next-Key Lock合力出手:既锁命中行,又锁它左边间隙,真正封死“该范围内增删改”的所有可能。
实战中最容易踩的坑
你以为只锁了 WHERE 条件里的值,其实锁了一整片区域;你以为查不到的记录就没事,结果它锁了整个空隙;你以为只是 SELECT,结果加了锁还阻塞 INSERT —— 这些都是 Next-Key Lock 的隐形杀伤力。
看似WHERE age = 25,但如果 age 是普通索引,且表里有 age=24 和 age=26,那它实际锁的是
(24, 26],别人插 age=25.5 也会被卡住
UPDATE ... WHERE只锁命中的行(
Record Lock),除非 WHERE 是范围条件;但
SELECT ... FOR UPDATE在 RR 级别下一定走 Next-Key,行为不一致 事务中执行
SELECT ... FOR UPDATE后忘了
COMMIT或
ROLLBACK,锁会一直挂着,后续 INSERT/UPDATE 可能无限等待,直到锁超时(默认 50 秒)
最复杂也最容易被忽略的一点:Next-Key Lock 的加锁范围高度依赖索引结构和现有数据分布,不是光看 SQL 就能推断出来的;想准确定位,得结合
EXPLAIN、
INFORMATION_SCHEMA.INNODB_LOCKS(或 8.0+ 的
performance_schema.data_locks)和实际数据值交叉验证。
