记录间隙临键锁

共 20 题
📑 题目列表 20 题
#
★★★

1. MySQL InnoDB 临键锁(Next-Key Lock),记录锁 + 间隙锁的组合?

MySQL InnoDB 的临键锁(Next-Key Lock)是什么?它为什么是记录锁与间隙锁的组合?

  • 临键锁的构成
  • 左开右闭区间
  • 防止幻读的作用

临键锁(Next-Key Lock)是 InnoDB 在 REPEATABLE READ 级别下,对索引记录及其前面间隙加锁的机制,其锁定的区间是"左开右闭"的,即 (前一条记录, 当前记录]。它实际上是记录锁(Record Lock)与间隙锁(Gap Lock)的组合:既锁住索引记录本身,也锁住该记录之前的间隙。这样既能防止其他事务修改/删除这条记录,也能防止其他事务在该间隙插入新记录,从而防止幻读。示例:若索引值有 10、20、30,对 20 加临键锁实际锁定 (10, 20] 区间。

临键锁是 InnoDB 解决 RR 下幻读的加锁手段。它把"锁行"扩展到"锁区间",从根上阻断新记录插入到已锁定的范围,从而在加锁读层面保证结果集稳定。

#
★★★

2. MySQL InnoDB 记录锁(Record Lock),锁定索引记录?

MySQL InnoDB 的记录锁(Record Lock)是什么?它如何锁定索引记录?

  • 记录锁的概念
  • 基于索引记录而非整行
  • 与间隙锁的区别

记录锁(Record Lock)是 InnoDB 最基本的行锁,它锁定的是索引记录(index record),而不是传统意义上的"数据行"。由于 InnoDB 的所有数据都通过索引组织(聚簇索引叶子节点即数据行),记录锁实际是锁在索引项上。UPDATE、DELETE 以及 SELECT ... FOR UPDATE 都会对访问到的索引记录加记录锁。记录锁只针对单条记录,不防止其他事务在间隙插入新记录,因此单独使用无法防止幻读。在唯一索引等值查询命中记录时,InnoDB 只加记录锁而无间隙锁。

记录锁是行锁的载体,理解"锁在索引上"是理解 InnoDB 加锁的关键。这也是为什么没有索引的查询会退化为全表加锁(锁所有记录)。

#
★★★

3. MySQL InnoDB 间隙锁(Gap Lock),锁定索引间隙防止插入?

MySQL InnoDB 的间隙锁(Gap Lock)是什么?它如何锁定索引间隙从而防止插入?

  • 间隙锁的概念
  • 锁定记录之间的空隙
  • 防止插入、防止幻读

间隙锁(Gap Lock)锁定的是索引记录之间的空隙,即"两条记录之间"或"边界到记录之间"的区间,它是开区间(不包含边界记录)。间隙锁的作用是阻止其他事务在锁定的间隙内插入新记录,从而防止幻读。由于间隙锁只针对"范围内不允许插入",它不阻止其他事务修改间隙两侧的已有记录。间隙锁之间不互相冲突(两个事务可以同时持有重叠的间隙锁),它们只与"插入意向锁"冲突,因为插入操作要插入意向锁。

间隙锁是临键锁的组成部分,其核心价值是"封锁插入"。理解间隙锁的冲突关系(与插入意向锁冲突、不与间隙锁互斥)是分析死锁与并发插入行为的基础。

#
★★★

4. READ COMMITTED 下 Gap Lock 禁用(外键约束与重复键检查除外)?

为什么 READ COMMITTED 下 InnoDB 会禁用 Gap Lock?外键约束与重复键检查为何是例外?

  • RC 下禁用间隙锁的原因
  • 外键与唯一键检查仍需间隙锁
  • 对幻读的影响

在 READ COMMITTED 下,InnoDB 会禁用间隙锁,只保留记录锁,目的是提高并发度、减少锁冲突。因为 RC 级别不保证可重复读,也不需要在加锁读层面完全防止幻读。但有两个例外:一是外键约束检查,当插入或更新子表记录时,需要锁定父表对应记录及其间隙,防止父表记录在检查期间被删除或修改导致引用不一致;二是唯一键/重复键检查,插入时需检查唯一索引,若存在重复键则需加锁防止并发插入冲突。这两个场景下间隙锁仍然会被使用。

RC 禁用间隙锁是"以并发换一致性"的取舍,但数据完整性约束(外键、唯一键)属于必须保证的,因此保留间隙锁。这也是为什么 RC 下仍可能出现死锁与锁等待,而不仅仅是记录锁。

#
★★★

5. REPEATABLE READ 下 Next-Key Lock 的应用,防止幻读?

REPEATABLE READ 下如何应用临键锁(Next-Key Lock)来防止幻读?

  • RR 下加锁读使用临键锁
  • 锁定范围防止插入
  • 快照读与加锁读对比

在 REPEATABLE READ 下,InnoDB 对加锁读(SELECT ... FOR UPDATE、UPDATE、DELETE)使用临键锁(Next-Key Lock),即对访问到的每条索引记录及其前面的间隙加锁。这样既锁住了已存在的记录,也锁住了它们之间的间隙,任何其他事务都无法在锁定的范围内插入新记录,从而在加锁读层面防止幻读。普通 SELECT(快照读)则通过复用同一 Read View 实现一致性,不依赖临键锁。因此 RR 下"防止幻读"由双保险保证:快照读靠快照,加锁读靠临键锁。

RR 下临键锁是防止幻读的加锁机制。它通过"封锁插入区间"来保证在一个事务的加锁读范围内结果集不变。理解这一点能解释为什么 RR 下 UPDATE/DELETE 会有较高锁冲突。

#
★★★

6. Next-Key Lock 的组成,记录锁+间隙锁如何锁住范围?

Next-Key Lock 的组成是什么?记录锁与间隙锁如何协同锁住一个范围?

  • 记录锁锁记录
  • 间隙锁锁记录之前的空隙
  • 左开右闭区间

Next-Key Lock 由两部分组成:记录锁(Record Lock)锁住当前索引记录,间隙锁(Gap Lock)锁住当前记录之前的间隙。两者合起来锁定的是"左开右闭"区间 (前一条记录, 当前记录]。例如索引值 10、20、30,对 20 加临键锁时,锁定 (10, 20],即索引记录 20 以及 10 到 20 之间的空隙。这样范围内既不能修改/删除记录 20,也不能在 (10,20) 之间插入新记录。当多个索引记录都被加临键锁时,连续的区间连起来就锁住了整个范围,彻底防止该范围内的插入与修改。

临键锁把"锁记录"扩展为"锁区间",是它区别于普通行锁的核心。记录锁与间隙锁分别负责"锁已有记录"与"锁未来插入",二者协同才能封锁整个范围。

#
★★★

7. 为什么 RR 隔离级别下普通 SELECT 不加锁(快照读),而 SELECT ... FOR UPDATE/UPDATE/DELETE 才加临键锁(当前读)

为什么 RR 隔离级别下普通 SELECT 不加锁(快照读),而 SELECT ... FOR UPDATE/UPDATE/DELETE 才加临键锁(当前读)?

  • 快照读与当前读的区别
  • RR 下加锁分工
  • 并发与一致性的权衡

在 RR 下,普通 SELECT 是快照读,基于 Read View 读取历史版本,不加任何锁,因此并发度高、不阻塞写,这是 MVCC 的核心理念。而 SELECT ... FOR UPDATE、UPDATE、DELETE 是当前读,需要读取最新已提交数据并进行修改,因此必须加锁(在 RR 下加临键锁)来保证修改的原子性与互斥,防止其他事务并发修改同一范围。这样设计实现了"读不加锁、写才加锁"的分工:查询走快照获得高并发,写操作加锁保证正确性。

快照读与当前读的区分是 InnoDB 并发控制的精髓。快照读用版本隔离换取读并发,当前读用锁换取写安全。理解这一分工就能解释为什么 RR 下普通 SELECT 不会产生锁等待,而 UPDATE 会。

#
★★★

8. 范围查询(>=、>、BETWEEN)的临键锁边界与锁区间推导

范围查询(>=、>、BETWEEN)的临键锁边界与锁区间如何推导?

  • 临键锁的区间推导
  • =、>、BETWEEN 的边界差异

  • 锁范围的确定

范围查询的临键锁边界要根据实际访问到的索引记录来确定。以索引记录 10、20、30 为例:查询 WHERE id >= 20 会加临键锁 (10,20]、(20,30],并在 30 之后加一个上界间隙锁 (30, +∞);查询 WHERE id > 20 会加临键锁 (20,30] 及上界间隙 (30, +∞),但不会锁 (10,20];查询 WHERE id BETWEEN 20 AND 30 等价于 id >= 20 AND id <= 30,会加临键锁 (10,20]、(20,30] 及上界间隙 (30, +∞)。推导的关键是:范围查询会锁住所有被扫描到的记录及其之间的间隙,并额外锁住序列末尾的上界间隙。

范围查询的加锁范围比等值查询更宽,常导致大范围锁冲突与死锁。推导时需结合具体索引值分布,明确"锁了哪些记录、哪些间隙、上界间隙到哪里"。

#
★★

9. 唯一索引等值查询仅使用 Record Lock 的特例?

为什么唯一索引等值查询命中记录时仅使用记录锁(Record Lock)而不加间隙锁?

  • 唯一索引等值查询的退化为记录锁
  • 无幻读风险
  • 与普通索引的差异

当 WHERE 条件命中唯一索引的等值查询且记录存在时,InnoDB 会将该临键锁退化为仅记录锁(Record Lock),因为唯一索引保证了该值只有一条记录,不存在"间隙插入另一条相同值记录"的幻读风险,无需再锁间隙。但如果唯一索引等值查询未命中记录(记录不存在),则仍需在间隙上加间隙锁,防止其他事务插入该值。对于普通索引(非唯一)的等值查询,即使命中记录,由于后面可能还有相同值的记录,仍需加临键锁。

唯一条目使"在间隙插入同值记录"不可能发生,因此可以省略间隙锁、缩小锁范围、提高并发。这一特例是 InnoDB 加锁优化的重要体现。

#
★★

10. 插入意向锁(Insert Intention Lock)的应用,多事务并发插入?

插入意向锁(Insert Intention Lock)是什么?它如何应用于多事务并发插入?

  • 插入意向锁的概念
  • 不同间隙的并发插入互不阻塞
  • 与间隙锁的冲突

插入意向锁(Insert Intention Lock)是间隙锁的一种特殊类型,它在插入操作时获取。当多个事务要往同一个间隙插入数据时,由于插入意向锁之间互不冲突,这些事务可以并发插入到同一间隙的不同位置,从而提升并发插入性能。只有当某事务已持有该间隙的间隙锁(或临键锁)时,插入意向锁才会与之冲突而等待。因此插入意向锁的作用是:让"无间隙锁保护的并发插入"能够并行,同时在与间隙锁冲突时正确排队。

插入意向锁是"插入"与"间隙锁"之间的协调机制。它允许并发插入(意向锁之间不互斥),但遇到间隙锁时让位等待,从而保证间隙锁封锁插入的语义。

#
★★

11. Gap Lock 的性能影响?

Gap Lock 对性能有什么影响?它为何会降低并发度?

  • 间隙锁扩大锁范围
  • 降低并发插入能力
  • 死锁与锁等待

Gap Lock 的性能影响主要体现在:它把锁从"单条记录"扩展到"一个区间",会阻止其他事务在区间内插入,从而降低并发度,尤其是在范围查询或非唯一索引等值查询较多时,容易造成大范围锁竞争和锁等待。此外,间隙锁之间不冲突但会阻塞插入意向锁,导致插入操作被阻塞,可能引发死锁。同时,间隙锁只在 RR 级别(及 SERIALIZABLE)下产生,RC 下禁用间隙锁即是为了减少这种锁竞争。因此,在可接受幻读或业务不依赖 RR 语义时,用 RC 级别可显著减少间隙锁带来的性能问题。

间隙锁性能代价的本质是"锁的范围扩大"与"插入被阻塞"。权衡并发与隔离是数据库调优的核心,RR 的高隔离语义以牺牲并发为代价。

#
★★

12. 间隙锁的冲突,为什么间隙锁之间不互斥,与插入意向锁的协作?

为什么间隙锁之间不互斥?间隙锁与插入意向锁如何协作?

  • 间隙锁之间不冲突
  • 间隙锁与插入意向锁冲突
  • 协作机制

间隙锁之间不互斥,因为两个事务同时持有同一个间隙的间隙锁,并不会造成数据不一致——间隙锁只表示"不允许插入",而两个事务都只是"禁止他人插入",彼此不冲突。真正与之冲突的是插入意向锁:当一个事务要插入记录时,会申请插入意向锁,若该间隙已被其他事务的间隙锁/临键锁锁住,插入意向锁就要等待。这种协作使间隙锁能阻止插入,而多个间隙锁本身又能共存,避免了不必要的锁竞争。总结:间隙锁 vs 间隙锁不冲突,间隙锁 vs 插入意向锁冲突。

间隙锁的冲突规则是"不互斥、只挡插入"。这一设计既保证了插入被封锁的语义,又允许多个事务同时对同一间隙做只读性封锁,减少了死锁与等待。

#
★★

13. 临键锁与唯一索引,唯一键冲突时的加锁与死锁?

临键锁与唯一索引交互时,唯一键冲突会如何加锁?可能引发什么死锁?

  • 唯一键冲突时的加锁
  • 重复键检查与插入意向锁
  • 死锁场景

当插入记录与唯一索引冲突时,InnoDB 会先对已存在的重复记录加共享锁(S 锁)以检查重复键,然后尝试插入。若插入失败,会产生重复键错误。这个"检查重复键加 S 锁"的过程可能与其他事务的插入意向锁、记录锁形成死锁。典型死锁场景:两个事务都尝试插入相同或相邻的唯一键值,各自先对对方要插入的间隙或记录加锁,然后互相等待对方的锁,形成循环等待。死锁时 InnoDB 会检测并回滚其中一个事务。

唯一键冲突的加锁并不是简单的插入失败,它涉及"先检查重复并加锁"的复杂流程,容易在并发插入唯一键时产生锁等待与死锁。解决思路是保证插入顺序一致、减少冲突范围。

#
★★

14. 外键约束检查触发的共享锁与子表插入时的锁等待

外键约束检查会触发什么锁?子表插入时为何会产生锁等待?

  • 外键检查加共享锁
  • 父表记录被锁时的等待
  • 锁等待与死锁

在 InnoDB 中,插入或更新子表记录时,若存在外键约束,会检查父表对应的主键记录,并对其加共享锁(S 锁)。如果父表该记录已被其他事务加排他锁(X 锁)或临键锁,子表插入就要等待,直到父表锁释放,从而产生锁等待。同理,删除或更新父表记录时,若存在引用的子表记录,也会对子表相关记录加锁,可能造成锁等待。这种因外键约束触发的锁等待在并发 DML 中常见,严重时可能升级为死锁。

外键约束通过加锁来保证引用完整性,这是"锁"在数据完整性上的典型应用。理解外键检查的加锁行为,有助于排查因外键导致的锁等待与死锁。

#
★★

15. 间隙锁与插入意向锁交互导致的死锁案例与日志解读

间隙锁与插入意向锁交互如何导致死锁?如何解读死锁日志?

  • 间隙锁与插入意向锁的冲突
  • 死锁产生过程
  • 死锁日志解读

典型的死锁场景:事务 A 对某个范围加间隙锁(如范围查询),事务 B 试图向该间隙插入记录,申请插入意向锁而等待;同时事务 A 又等待事务 B 持有的其他锁,形成循环等待。另一种常见:事务 A 和 B 分别对两个间隙加锁,然后各自要插入到对方锁住的间隙,形成交叉等待。死锁时 InnoDB 会自动检测并回滚代价较小的一方。通过 SHOW ENGINE INNODB STATUS 或错误日志中的死锁信息,可以看到两个事务的锁等待链(LATEST DETECTED DEADLOCK),包括各自持有的锁类型、等待的锁类型、涉及的记录与事务号,据此定位问题。

间隙锁与插入意向锁的交互是 InnoDB 死锁的高发来源。解读死锁日志时,重点是看两个事务"持有什么锁、等待什么锁",以及它们是否形成循环,从而设计规避方案(如统一加锁顺序)。

#
★★

16. InnoDB 对二级索引与聚簇索引的加锁顺序(先二级索引再回表加主键锁)与死锁的关系

InnoDB 对二级索引与聚簇索引的加锁顺序是什么?这与死锁有何关系?

  • 二级索引加锁再回表加主键锁
  • 加锁顺序不一致导致死锁
  • 规避方法

InnoDB 更新数据时,会先对二级索引记录加锁,再通过回表(回表查询)对聚簇索引(主键)记录加锁,即"先二级索引、后聚簇索引"。如果多个事务以不同的顺序访问不同的二级索引和聚簇索引记录,就可能形成循环等待从而死锁。例如事务 A 先锁二级索引 a 再锁主键记录,事务 B 先锁主键记录再锁二级索引 a,二者互相等待对方已持有的锁。这种因加锁顺序不一致导致的死锁是 InnoDB 最常见的死锁原因之一。规避手段是让所有事务按一致顺序访问索引。

加锁顺序是死锁分析的经典框架。InnoDB 的"先二级索引、后聚簇索引"以及不同事务加锁顺序不一致,是死锁产生的重要来源。统一加锁顺序是预防死锁的有效手段。

#

17. 如何查看与减少间隙锁,RR 下的范围查询加锁分析?

如何查看与减少间隙锁?RR 下的范围查询加锁如何分析?

  • 查看间隙锁的途径
  • 减少间隙锁的方法
  • 范围查询加锁分析

查看间隙锁可通过 performance_schema 的 data_locks 表(MySQL 8.0)或 SHOW ENGINE INNODB STATUS,其中 LOCK_TYPE 显示为 RECORD 且 LOCK_MODE 含 GAP 即表示间隙锁/临键锁。减少间隙锁的方法包括:将隔离级别改为 READ COMMITTED(禁用间隙锁)、使用唯一索引等值查询(退化为记录锁)、缩小范围查询的扫描范围、利用索引让查询更精确命中记录。分析 RR 下范围查询加锁时,要结合执行计划确认实际使用的索引与扫描到的记录范围,再推导锁定的间隙。

间隙锁的观测与优化是实际性能调优的一部分。通过监控工具定位间隙锁,再通过隔离级别、索引设计等手段减少间隙锁,是提升并发与降低锁冲突的常用思路。

#

18. 如何验证加锁范围,performance_schema 与锁监控?

如何验证加锁范围?performance_schema 与锁监控如何实现?

  • performance_schema 的 data_locks/data_lock_waits
  • 验证锁范围
  • 锁监控视图

在 MySQL 8.0 中,可通过 performance_schema 的 data_locks 表查看当前持有的锁,包括锁类型、锁模式(X/S)、锁定的索引与记录(通过 LOCK_MODE、LOCK_DATA 字段);data_lock_waits 表查看锁等待关系。结合这两个表可以验证某条 SQL 实际锁定的范围与锁等待情况。此外 information_schema.innodb_trx 可查看事务状态,SHOW ENGINE INNODB STATUS 可看死锁与锁信息。通过这些监控手段,可以精确验证加锁范围是否与预期一致。

performance_schema 是 MySQL 8.0 的锁监控主视图,替代了旧版的 information_schema.innodb_locks。掌握这些视图能帮助定位锁范围异常、锁等待与死锁问题。

#

19. 如何通过 EXPLAIN/锁监控确认 UPDATE 实际锁定的索引范围

如何通过 EXPLAIN 或锁监控确认 UPDATE 实际锁定的索引范围?

  • EXPLAIN 查看执行计划与索引
  • 结合锁监控确认锁范围
  • 实际加锁范围

通过 EXPLAIN 查看 UPDATE 的执行计划,可以确认其使用的索引(type 列)与扫描方式(如 ref、range、index),从而推断可能访问的记录范围。但 EXPLAIN 只显示"计划访问范围",实际加锁范围还需结合锁监控(performance_schema.data_locks)确认,因为加锁范围取决于实际扫描到的记录及间隙。例如一个没有合适索引的 UPDATE 会走全表扫描,锁住所有记录与间隙,造成大范围锁。因此,优化 UPDATE 的加锁范围应从"建立合适索引"入手,让扫描命中更精确的区间。

EXPLAIN 给出"访问计划",锁监控给出"实际加锁",两者结合才能完整判断 UPDATE 的锁范围。优化锁范围的关键是合理索引,避免全表扫描导致的大范围加锁。

#

20. 锁等待超时的观测(information_schema.INNODB_TRX 与 performance_schema.data_lock_waits)

如何观测锁等待超时?information_schema.INNODB_TRX 与 performance_schema.data_lock_waits 如何配合?

  • INNODB_TRX 查看事务状态
  • data_lock_waits 查看等待关系
  • 锁等待超时定位

当发生锁等待超时(Lock wait timeout exceeded)时,可通过 information_schema.innodb_trx 查看各事务状态,包括 trx_id、trx_state、trx_started、trx_rows_locked 等,定位哪个事务在等待、被谁阻塞;通过 performance_schema.data_lock_waits 查看锁等待的具体关系(who holds / who waits)。结合两者,可以找出阻塞链的源头事务,必要时 kill 该事务释放锁。innodb_lock_wait_timeout 控制等待超时时间(默认 50 秒),超时后等待方事务回滚等待的语句。

锁等待超时是生产环境的常见问题,正确观测需要"事务视图 + 锁等待视图"双管齐下。定位并终止阻塞源事务是解决锁等待的直接手段。