唯一索引等值查询未命中时仅锁间隙(X,GAP),如id=999不存在则锁(100,+∞);非唯一索引未命中则加更重的next-key锁(X型,左开右闭);无索引或RC级别下间隙锁失效。

因为InnoDB必须在RR隔离级别下阻止幻读,而“未命中”本身不等于“无扫描范围”——只要走索引定位到了间隙,就要锁住它。
唯一索引等值查询未命中:只锁间隙,不锁记录
当执行 SELECT * FROM t WHERE id = 999 FOR UPDATE(id 是主键),而表中最大 id 是 100 时,InnoDB 不会什么都不做。它会沿 B+ 树找到插入点:前一条是 id = 100,后一条本该是 id = 999,但不存在,于是锁定区间 (100, +∞) —— 注意是开区间,不包含 100 和 999 本身。
- 这个锁是
X,GAP类型,不是X,REC_NOT_GAP,也不是X(即 next-key) - 它阻塞
INSERT INTO t VALUES (500, ...),但不阻塞UPDATE t SET x=1 WHERE id = 100 - 如果误以为“没查到就不锁”,在高并发 INSERT 场景下可能突然出现长时间等待,却查不到活跃事务持有记录锁
非唯一索引等值查询未命中:加的是 next-key 锁,范围更大
若 status 是普通索引,执行 SELECT * FROM t WHERE status = 'pending' FOR UPDATE,而表中没有 status = 'pending' 的行,InnoDB 无法精确定界,会向右扫描直到确认无匹配,最终可能锁住 ('done', 'success') 这样的区间(取决于索引顺序)。
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
- 此时加的不是纯 gap lock,而是
X类型的 next-key 锁,范围是左开右闭,如('done', 'success'] - 它既锁间隙,也锁右侧边界记录(如果存在),所以比唯一索引场景更重
- EXPLAIN 显示
type = ref并不能保证只锁一行;key_len小、rows估算不准时,实际扫描和加锁范围可能远超预期
没走索引 or 隔离级别是 RC:行为完全不同
间隙锁不是万能的,它的存在高度依赖两个前提:索引扫描 + RR 隔离级别。一旦打破,机制就切换。
- WHERE 条件没走索引(
type = ALL)→ 全表扫描,每条聚簇索引记录加X,REC_NOT_GAP,效果接近表锁 - 会话设为
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED→ 间隙锁禁用,UPDATE ... WHERE id = 999真的不加任何行级锁(但语句仍会加意向锁) -
SELECT ... FOR UPDATE在 RC 下可能返回空集并立即释放锁,而 RR 下即使空也会持锁直到事务结束
真正容易被忽略的是:你看到的“没查到数据”,在 InnoDB 眼里只是“没匹配到记录”,但它早已根据索引结构划出了必须守护的疆界。这个疆界不会出现在 INFORMATION_SCHEMA.INNODB_TRX 里,得靠 SELECT * FROM performance_schema.data_locks 或 SHOW ENGINE INNODB STATUS\G 中的 lock_mode X locks gap before rec 才能确认。

















