间隙锁范围由索引结构和SQL实际访问的索引点决定,锁定相邻索引值间的开区间,需通过EXPLAIN确认所用索引及扫描类型,再依B+树前驱后继确定具体间隙。

间隙锁(Gap Lock)的范围不是凭空确定的,而是严格依赖于索引结构和当前事务中执行的 SQL 语句所涉及的索引记录。它的核心逻辑是:锁定**两个相邻索引值之间的“间隙”**,不包括记录本身(那是行锁的事),也不包括无穷边界以外的部分。要准确计算间隙锁范围,必须先搞清语句命中了哪个索引、扫描到了哪些索引点、以及这些点在索引树中的前驱与后继是谁。
看执行计划,确认走的是哪个索引
间隙锁只作用于被实际使用的索引。如果 SQL 没走索引(例如全表扫描),InnoDB 在可重复读下可能升级为临键锁(Next-Key Lock),但不会单纯加间隙锁。所以第一步永远是 EXPLAIN:
- 检查 key 列是否非 NULL,确认走了哪个索引(PRIMARY、idx_name、idx_age 等)
- 看 type 是 range、ref 还是 eq_ref,判断是单点查找还是范围扫描
- 注意 possible_keys 和 key 是否一致,避免因隐式类型转换导致索引失效
按索引顺序列出所有“触达”的索引值
所谓“触达”,指查询条件在索引 B+ 树中定位或遍历时真正访问到的索引项。例如:
- SELECT * FROM t WHERE age > 18 AND age —— 若 age 有索引,且现有数据中 age 值为 20、22、24,则扫描覆盖的索引点是 20、22、24;间隙锁会落在 (−∞,20)、(20,22)、(22,24)、(24,+∞) 中被扫描影响的部分
- SELECT * FROM t WHERE name > 'Li' AND name —— 需把索引中实际存在的 name 值(如 'Liu', 'Lu', 'Wu')按字典序排出来,再找相邻间隙
- 等值查询 WHERE id = 100 不加间隙锁(只加记录锁),但如果写成 WHERE id > 99 AND id ,哪怕只命中一行,也会锁住 (99,100) 和 (100,101) 两个间隙
识别前驱与后继,确定实际锁定的开区间
间隙锁的本质是防止其他事务在该间隙中插入新记录,因此它总是以“前一个已存在索引值”和“后一个已存在索引值”为边界。关键细节:
- 若查询范围左端无对应索引记录(如 age > 100,而最大 age 是 95),则左边界为前一个最大值,右边界为 +∞ → 锁 (95, +∞)
- 若查询扫到索引最左端(如 name ,而最小 name 是 'Bao'),则锁 (−∞, 'Bao')
- 唯一索引上的等值查询(WHERE uniq_col = x)不加间隙锁;但普通索引上的相同语句,在 RR 隔离级别下会加 Next-Key 锁,即 record lock + gap lock,覆盖 (prev, x]
注意唯一索引 vs 普通索引的行为差异
这是最容易出错的地方:
- 唯一索引(含主键):等值查询命中记录 → 只加记录锁;未命中 → 加间隙锁,范围是该值在索引顺序中应处位置的前后间隙(例如查 id=50,但表中只有 49 和 51,则锁 (49,51))
- 普通索引:即使等值查询命中,只要不是唯一约束,InnoDB 默认使用 Next-Key Lock(record + gap),即锁住 (prev, current];若配合 SELECT ... FOR UPDATE 或更新语句,还可能进一步扩大
- 联合索引要按最左前缀匹配,间隙边界由整个索引元组决定,比如 idx(a,b),查 WHERE a = 1 AND b > 5,间隙基于 (1,5)、(1,7)、(1,10) 这样的组合值来算










