Skip to Content
面试题库Locks[面试题] SELECT FOR UPDATE忘加索引会发生什么?间隙锁怎么导致死锁?

[面试题] SELECT FOR UPDATE忘加索引会发生什么?间隙锁怎么导致死锁?

版块 A:知识讲解 (Knowledge Recap)

InnoDB 行级锁的三把锁:

锁类型锁住的范围触发场景
记录锁 (Record Lock)锁住具体某一行记录唯一索引等值查询,且数据存在
间隙锁 (Gap Lock)锁住两条记录之间的间隙(防止插入)普通索引等值查询 / 等值查询数据不存在
临键锁 (Next-Key Lock)记录锁 + 间隙锁的组合InnoDB 默认加锁单位(RR隔离级别下)

两个关键的锁退化规则:

  • 唯一索引 + 等值查询 + 数据存在 → Next-Key Lock 退化为 Record Lock(不锁间隙)
  • 任意索引 + 等值查询 + 数据不存在 → Next-Key Lock 退化为 Gap Lock(只锁间隙)

致命陷阱:Gap Lock 不互斥!两个事务可以同时持有同一范围的 Gap Lock,但都想插入数据时 → 互相等待 → 死锁 无索引的 SELECT FOR UPDATE:where 条件没有索引 → 全表扫描 → 每行都加 Next-Key Lock → 等价于表锁


版块 B:面试实战 (Combat Script)

Q:InnoDB 的行锁锁的到底是什么?SELECT FOR UPDATE 忘了加索引会怎样?

破局思路:面试官在考察你是否真正理解”行锁锁索引”这个本质。能从这里延伸到间隙锁死锁的真实案例,才是高手的标志。

💬 高手逐字稿

关于这个问题,核心结论是:InnoDB 的行锁,锁的是索引,而不是数据本身。

这带来了一个非常重要的推论:如果你的 SELECT FOR UPDATE 的 where 条件里没有走索引,InnoDB 没办法精确锁住某一行,就只能退化成全表扫描,把每一行、每一个间隙全部加锁,效果上等价于表级锁,并发性能直接崩掉。

说到加锁,我想延伸讲一下间隙锁导致死锁的问题,因为这是我们在生产环境中真实踩过的坑。

InnoDB 在 RR 隔离级别下,加锁的基本单位是临键锁(Next-Key Lock)。但有一个特殊情况:当等值查询没有命中任何数据时,临键锁会退化成间隙锁(Gap Lock),只锁住那段间隙,不锁记录。

这里有一个很多人不知道的关键细节,那就是:间隙锁之间是不互斥的。两个事务可以同时持有同一段间隙的 Gap Lock。

这就导致了一个经典的死锁场景:事务 A 和事务 B 都通过 SELECT FOR UPDATE 查询了一条不存在的数据,于是都拿到了同一段间隙的 Gap Lock。接着,事务 A 要插入数据,被事务 B 的 Gap Lock 阻塞;同时事务 B 也要插入,被事务 A 的 Gap Lock 阻塞。两边互相等待,死锁就发生了。

解决方案有两个:第一,把隔离级别降到 RC,RC 下没有幻读问题,自然就不会有 Gap Lock;第二,用分布式锁在业务层保证串行化,彻底绕开数据库锁的竞争。

最后更新于:
⏱️

登录后开始阅读

当前 MVP 版本只开放邮箱密码登录注册。完成登录后即可进入面试题内容。

还没有账号?先去注册