唯一索引等值查询命中记录时不加间隙锁,因next-key锁退化为纯记录锁;未命中时(如查id=50但最大id为49)则锁定间隙(49,+∞)。

唯一索引等值查询不加间隙锁的底层原因
因为InnoDB对唯一索引的等值查询(命中记录)会将next-key lock直接退化为纯record lock,只锁住那一条确定存在的记录,不覆盖任何间隙。这是由加锁基本单位和优化规则共同决定的:加锁单位确实是next-key lock(左开右闭区间),但一旦确认查询条件能**唯一、精确匹配到某一行**,且该行**真实存在**,InnoDB就不再需要防止“在间隙里插新行”——幻读风险不存在,锁就收窄。
什么情况下唯一索引等值查询反而会加间隙锁
两种反直觉但高频踩坑的场景:
-
SELECT ... FOR UPDATE WHERE id = 50,但表中最大id是49 → InnoDB向右找不到匹配值,锁定间隙(49, +∞) -
WHERE id IN (10, 15, 20)→ 看似等值,实为多次单点查询,每次都是record lock,不触发间隙锁;但若写成WHERE id > 10 AND id ,哪怕数据恰好只有15,也会锁(10, 20)
怎么验证当前SQL到底加了什么锁
别猜,查performance_schema.data_locks:
SELECT ENGINE_TRANSACTION_ID, INDEX_NAME, LOCK_TYPE, LOCK_MODE, LOCK_DATA FROM performance_schema.data_locks WHERE LOCK_MODE LIKE '%GAP%' OR LOCK_MODE = 'RECORD';
关键看LOCK_MODE字段:RECORD表示行锁,GAP或NEXT-KEY才含间隙成分;LOCK_DATA显示的是索引值端点,比如5, 10代表间隙(5, 10),不是某行的id=5。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
容易被忽略的隐性前提:隔离级别和索引有效性
即使写了WHERE id = ?,也必须同时满足:
- 当前事务隔离级别是
REPEATABLE READ(执行SELECT @@transaction_isolation确认) -
id列必须是**唯一索引**(主键或UNIQUE约束),普通索引不适用此退化规则 - 执行计划必须走索引(用
EXPLAIN看key列非NULL,type不是ALL)
三者缺一,就可能退化为全表扫描+表锁,或者升级为next-key lock——这时候你以为的“等值查询”,实际已不是那个意思了。










