间隙锁在REPEATABLE READ级别下,由非唯一索引或范围查询触发,锁定索引间隙而非记录本身;唯一索引等值查询(=、非NULL、索引有效)时通常不加间隙锁。

间隙锁(Gap Lock)在什么情况下会被触发
MySQL 的间隙锁只在 REPEATABLE READ 隔离级别下由 InnoDB 主动加,且仅作用于非唯一索引或范围查询场景。它不会锁住记录本身,而是锁定索引项之间的“空隙”,防止其他事务在这个范围内插入新行。
常见触发条件包括:
-
SELECT ... FOR UPDATE或SELECT ... LOCK IN SHARE MODE配合范围条件(如WHERE age > 25),且age列无唯一索引 -
UPDATE/DELETE使用非唯一索引做范围扫描(如UPDATE users SET name='x' WHERE status=1,status是普通索引) - 即使查询结果为空,只要走的是范围扫描 + 非唯一索引,InnoDB 仍会加间隙锁(例如
SELECT * FROM t WHERE id > 100 FOR UPDATE,而表中最大id是 99)
唯一键锁(Record Lock + Gap Lock 组合)为什么有时不生效
当查询条件命中唯一索引(含主键)的**等值匹配**时,InnoDB 通常只加记录锁(Record Lock),不加间隙锁——这是优化,不是 bug。但这个“只锁记录”的行为有严格前提:
- 必须是
=查询,不能是>=、或BETWEEN - 唯一索引列不能为
NULL(否则可能退化为间隙+记录组合) - 查询必须能通过索引直接定位到单条记录(即执行计划显示
type=const或type=eq_ref) - 如果使用了
OR、函数、隐式类型转换(如WHERE id = '123'而id是整型),可能导致索引失效,进而触发间隙锁
典型反例:
SELECT * FROM orders WHERE order_no = 'ORD-001' FOR UPDATE;若
order_no 是唯一索引但定义为 VARCHAR,而传入参数是数字类型(如 123),MySQL 会做隐式转换,导致无法使用索引,最终锁住整个范围。
如何验证当前语句加了哪种锁
最直接的方式是查 INFORMATION_SCHEMA.INNODB_TRX 和 INFORMATION_SCHEMA.INNODB_LOCKS(MySQL 5.7)或 performance_schema.data_locks(MySQL 8.0+)。但更实用的是结合 SHOW ENGINE INNODB STATUS\G 中的 TRANSACTIONS 部分观察锁信息。
ECTouch是上海商创网络科技有限公司推出的一套基于 PHP 和 MySQL 数据库构建的开源且易于使用的移动商城网店系统!应用于各种服务器平台的高效、快速和易于管理的网店解决方案,采用稳定的MVC框架开发,完美对接ecshop系统与模板堂众多模板,为中小企业提供最佳的移动电商解决方案。ECTouch程序源代码完全无加密。安装时只需将已集成的文件夹放进指定位置,通过浏览器访问一键安装,无需对已有
关键识别点:
- 输出中出现
gap before rec表示间隙锁 - 出现
record lock且带具体主键值(如n_fields=2; compact format; info bits 0; 0: len 4; hex 80000001; asc ;;)表示记录锁 - 出现
supremum pseudo-record表示锁住了索引末尾间隙(常被忽略,但极易引发死锁) - 若看到
lock_mode X locks gap before rec insert intention,说明另一个事务正尝试在该间隙插入——这是典型的插入冲突信号
唯一索引等值查询却发生死锁?重点检查这些地方
即使用了唯一索引等值查询,仍可能因以下原因卷入死锁:
- 事务中先查后更新,但两次查询使用不同索引(如第一次用
email唯一索引查用户,第二次用user_id更新订单),导致锁顺序不一致 - 唯一索引字段存在重复
NULL值(MySQL 中NULL != NULL,唯一约束允许多个NULL,此时 InnoDB 会将它们视作“相同间隙”处理) - 使用了
INSERT ... ON DUPLICATE KEY UPDATE:它先尝试插入,失败时转为更新,过程中可能先加插入意向锁(insert intention lock),再加记录锁,形成锁升级路径 - 应用层重试逻辑未设超时或未捕获
Deadlock found when trying to get lock错误码(1213),导致重试雪崩
真正难调试的,往往是那些看似“只锁一行”的语句,在并发插入场景下,因为间隙锁与插入意向锁的交互规则被绕开,实际锁住了比预期更大的范围。









