You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Gap Locks未出现在data_locks表中?已验证间隙插入被拦截

为何基于唯一索引的范围查询间隙锁未在performance_schema.data_locks中显示?

操作步骤与现象

窗口1执行语句

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from table1 where id > 99 FOR UPDATE;
+-----+--------+--------+-------+------+
| id  | field1 | field2 | field | a    |
+-----+--------+--------+-------+------+
| 101 | hola   | NULL   | NULL  | NULL |
| 103 | yo     | NULL   | NULL  | NULL |
| 107 | hey    | NULL   | NULL  | NULL |
+-----+--------+--------+-------+------+
3 rows in set (0.00 sec)

窗口2查询锁信息

mysql> SELECT thread_id, event_id,
    -> object_schema, object_name, index_name,
    -> lock_type, lock_mode, lock_status, lock_data
    -> FROM performance_schema.data_locks
    -> WHERE thread_id = 279\G;
*************************** 1. row ***************************
    thread_id: 279
     event_id: 28
object_schema: laravel_doc
  object_name: table1
   index_name: NULL
    lock_type: TABLE
    lock_mode: IX
  lock_status: GRANTED
    lock_data: NULL
*************************** 2. row ***************************
    thread_id: 279
     event_id: 28
object_schema: laravel_doc
  object_name: table1
   index_name: PRIMARY
    lock_type: RECORD
    lock_mode: X
  lock_status: GRANTED
    lock_data: supremum pseudo-record
*************************** 3. row ***************************
    thread_id: 279
     event_id: 28
object_schema: laravel_doc
  object_name: table1
   index_name: PRIMARY
    lock_type: RECORD
    lock_mode: X
  lock_status: GRANTED
    lock_data: 101
*************************** 4. row ***************************
    thread_id: 279
     event_id: 28
object_schema: laravel_doc
  object_name: table1
   index_name: PRIMARY
    lock_type: RECORD
    lock_mode: X
  lock_status: GRANTED
    lock_data: 103
*************************** 5. row ***************************
    thread_id: 279
     event_id: 28
object_schema: laravel_doc
  object_name: table1
   index_name: PRIMARY
    lock_type: RECORD
    lock_mode: X
  lock_status: GRANTED
    lock_data: 107
5 rows in set (0.00 sec)

原表数据

+-----+--------+--------+-------+------+
| id  | field1 | field2 | field | a    |
+-----+--------+--------+-------+------+
|   3 | hello  | 1      | NULL  | NULL |
|  97 | hi     | 2      | NULL  | NULL |
| 101 | hola   | 3      | NULL  | NULL |
| 103 | yo     | 4      | NULL  | NULL |
| 107 | hey    | 5      | NULL  | NULL |
+-----+--------+--------+-------+------+

核心矛盾

已验证窗口2无法插入id=102的数据,说明存在间隙锁拦截,但performance_schema.data_locks表中未显示Gap Locks。

根据MySQL官方文档:

对于锁定读(带FOR UPDATE或FOR SHARE的SELECT)、UPDATE和DELETE语句,锁的获取取决于语句是否使用唯一索引的唯一搜索条件,还是范围型搜索条件。
对于使用唯一索引的唯一搜索条件,InnoDB仅锁定找到的索引记录,不锁定其前的间隙。
对于其他搜索条件以及非唯一索引,InnoDB会锁定扫描的索引范围,使用Gap Locks或Next-Key Locks来阻止其他会话向范围内的间隙插入数据。有关Gap Locks和Next-Key Locks的信息,请参阅第15.7.1节“InnoDB Locking”。

这是基于唯一索引的范围查询,并非唯一搜索条件,按文档逻辑应该显示Gap Locks,为何未出现?

原因分析

  1. Next-Key Lock的显示逻辑:InnoDB的Next-Key Lock是记录锁+间隙锁的组合,但performance_schema.data_locks不会单独显示间隙锁,而是通过记录锁的范围特性隐含体现。对于唯一索引的范围查询,InnoDB会对符合条件的记录加记录锁,同时对记录之间的间隙、最后一条记录到supremum伪记录的间隙加间隙锁,但这些间隙锁不会单独生成锁条目。

  2. 锁的优化与显示限制:当使用唯一索引进行范围查询时,InnoDB会优化锁的展示——仅显示实际存在的记录锁,间隙锁的存在只能通过插入拦截行为验证,不会直接在data_locks中列出。这是因为data_locks主要记录实际获取的锁对象,而间隙锁属于“隐式”的范围锁,无需单独生成条目。

  3. supremum伪记录的作用:你看到的lock_data: supremum pseudo-record的X锁,已经包含了从最后一条记录(107)到伪记录之间的间隙锁,阻止插入大于107的id;而101、103、107之间的间隙锁,是通过记录锁的Next-Key特性实现的,不需要单独显示。

验证方式

尝试插入id=98(97和101之间的间隙),同样会被拦截,这也能证明间隙锁的存在,只是未在data_locks中单独展示。


内容的提问来源于stack exchange,提问作者user19481364

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 06:40:26