为何Gap Locks未出现在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,为何未出现?
原因分析
Next-Key Lock的显示逻辑:InnoDB的Next-Key Lock是记录锁+间隙锁的组合,但
performance_schema.data_locks不会单独显示间隙锁,而是通过记录锁的范围特性隐含体现。对于唯一索引的范围查询,InnoDB会对符合条件的记录加记录锁,同时对记录之间的间隙、最后一条记录到supremum伪记录的间隙加间隙锁,但这些间隙锁不会单独生成锁条目。锁的优化与显示限制:当使用唯一索引进行范围查询时,InnoDB会优化锁的展示——仅显示实际存在的记录锁,间隙锁的存在只能通过插入拦截行为验证,不会直接在
data_locks中列出。这是因为data_locks主要记录实际获取的锁对象,而间隙锁属于“隐式”的范围锁,无需单独生成条目。supremum伪记录的作用:你看到的
lock_data: supremum pseudo-record的X锁,已经包含了从最后一条记录(107)到伪记录之间的间隙锁,阻止插入大于107的id;而101、103、107之间的间隙锁,是通过记录锁的Next-Key特性实现的,不需要单独显示。
验证方式
尝试插入id=98(97和101之间的间隙),同样会被拦截,这也能证明间隙锁的存在,只是未在data_locks中单独展示。
内容的提问来源于stack exchange,提问作者user19481364

