游乐游手机版
首页/数据库/文章详情

MySQL间隙锁是什么及触发条件详解

时间:2026-08-17 21:42
MySQL 间隙锁只会在 RR(可重复读)隔离级别下生效,主要用于避免幻读。它通常出现在当前读场景中,例如 FOR UPDATE、UPDATE、DELETE,并且需要配合索引范围查询,或查询目标记录不存在时才会触发,本质上锁住的是索引记录之间的开区间。间隙锁只在 RR 隔离级别下生效在 MySQL

MySQL 间隙锁只会在 RR(可重复读)隔离级别下生效,主要用于避免幻读。它通常出现在当前读场景中,例如 FOR UPDATE、UPDATE、DELETE,并且需要配合索引范围查询,或查询目标记录不存在时才会触发,本质上锁住的是索引记录之间的开区间。

什么是MySQL间隙锁及其触发条件?

间隙锁只在 RR 隔离级别下生效

在 MySQL 中,间隙锁(Gap Lock)并不会在所有情况下都启用,它有明确的生效条件:首先,只有当事务隔离级别为 REPEATABLE READ 时,InnoDB 才会使用这种锁机制;其次,它只作用于「当前读」相关语句,比如带有 FOR UPDATE、LOCK IN SHARE MODE、UPDATE 或 DELETE 的 SQL。

如果数据库运行在 READ COMMITTED 隔离级别下,InnoDB 通常会退化为只给实际命中的行加行锁,一般不会额外加间隙锁(唯一索引等值查询未命中属于少数例外)。因此,排查 MySQL 间隙锁是否生效时,第一步应先确认当前事务隔离级别:SELECT @@transaction_isolation;。

  • 在 RR 级别下,间隙锁是 InnoDB 的默认机制之一,核心目的是防止幻读
  • 在 RC 级别下,即使执行 SELECT * FROM t WHERE id > 10 FOR UPDATE,通常也不会锁住 (10, +∞) 这个间隙范围
  • 如需显式设置隔离级别,可使用: SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

哪些 SQL 会触发间隙锁?

并不是所有带条件的 FOR UPDATE 查询都会触发 MySQL 间隙锁,关键要看是否扫描到了索引空隙,以及使用的是哪种索引和查询方式。

常见触发场景包括:

  • SELECT * FROM users WHERE age > 20 AND age < 30 FOR UPDATE; —— 普通索引上的范围查询,通常会加间隙锁,锁定 (20,30) 区间
  • SELECT * FROM users WHERE id = 100 FOR UPDATE; —— 主键或唯一索引等值查询时,如果 id=100 这条记录不存在,就会锁住 (prev_id, next_id) 之间的间隙
  • SELECT * FROM users WHERE name LIKE 'zhang%'; —— 如果 name 字段上有普通索引,前缀匹配本质上仍然属于范围扫描,也可能触发间隙锁
  • UPDATE users SET status=1 WHERE created_at > '2025-01-01'; —— 范围更新语句同样可能触发间隙锁,前提是该条件走索引

反过来看,像 SELECT * FROM users WHERE id = 5 FOR UPDATE; 这种场景,如果 id 是主键且该记录真实存在,那么通常只会加记录锁,而不会加间隙锁。

间隙锁锁的是什么?不是记录,是“能插进去的地方”

理解 MySQL 间隙锁的关键在于:它锁住的并不是现有记录本身,而是索引中两个相邻值之间可以插入新数据的位置。比如索引值为 [10, 20, 30],那么对应的间隙就是 (-∞, 10)、(10, 20)、(20, 30)、(30, +∞)。这些位置虽然没有真实行数据,但都具备插入新记录的可能。

典型表现如下:

  • 事务 A 执行 SELECT * FROM t WHERE id > 10 AND id < 20 FOR UPDATE; → 锁住 (10, 20) 这一段间隙
  • 此时事务 B 再执行 INSERT INTO t (id, ...) VALUES (15, ...); → 会被阻塞,因为 15 落在被锁住的区间内
  • 但如果插入的是 INSERT INTO t (id, ...) VALUES (10, ...); 或 (20, ...),则不会因为间隙锁直接阻塞,因为端点属于已有记录,是否冲突由记录锁决定

需要注意的是,间隙锁对应的是「左开右开」区间,也就是不包含边界值;而临键锁(Next-Key Lock)则是「左开右闭」,即 (10, 20]。可以把它理解为“记录锁 + 前向间隙锁”,这也是 InnoDB 默认更常见的加锁模式。

为什么线上容易因间隙锁卡死或死锁?

单独的间隙锁本身并不是互斥的,多个事务甚至可以同时持有同一个间隙上的间隙锁。真正容易引发阻塞、卡死甚至死锁的,往往是「间隙锁 + 记录锁」组合形成的临键锁,以及不同事务在扫描顺序不一致时发生的竞争。

常见的高风险场景有:

  • 查询条件没有索引,例如 WHERE status=1 且 status 字段未建索引 → 会触发全表扫描 → 实际可能锁住整个聚簇索引范围,效果非常接近表锁
  • 范围条件过宽,比如 WHERE create_time > '2020-01-01' → 可能锁住从某条记录一直到 +∞ 的大范围间隙,导致后续插入操作大量阻塞
  • 两个事务分别执行 SELECT ... WHERE id > 100 FOR UPDATE 和 SELECT ... WHERE id > 90 FOR UPDATE,如果扫描顺序或索引访问路径不同,就可能形成循环等待
  • 业务中频繁使用 INSERT ... ON DUPLICATE KEY UPDATE,在唯一键冲突时也可能隐式触发间隙锁,尤其是冲突点落在某个间隙范围内时更明显

很多线上问题的难点,并不只是“这条 SQL 是否加了锁”,而是“它到底锁住了多大范围”。排查 MySQL 间隙锁、锁等待和死锁时,重点应查看 INFORMATION_SCHEMA.INNODB_TRX 与 INNODB_LOCKS(MySQL 8.0+ 已移除后者,改用 performance_schema.data_locks),而不能只停留在 SQL 表面写法上。

来源:https://www.php.cn/faq/2993053.html
上一篇MySQL DELETE语句用法详解与删除数据技巧 下一篇MySQL二进制日志保留时间设置与控制方法
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
Redis是什么:核心特性、架构与应用场景解析
数据库 · 2026-09-01

Redis是什么:核心特性、架构与应用场景解析

Redis是一款基于内存的键值型NoSQL数据库,以超高读写速度和丰富的数据结构著称。本文系统梳理Redis的核心特性、架构组成、性能优势及典型应用场景,并通过与Memcached、MySQL、MongoDB的对比,帮助开发者快速判断Redis是否适合当前业务需求。

Windows 安装 MongoDB 完整图文教程
数据库 · 2026-09-01

Windows 安装 MongoDB 完整图文教程

本文详细介绍在 Windows 系统上安装 MongoDB 的完整流程。从官网下载 MSI 安装包开始,逐步演示自定义安装路径、配置 Windows 服务、跳过 MongoDB Compass 等关键选项,并提供通过系统服务列表验证安装是否成功的方法,帮助开发者快速搭建本地 MongoDB 环境。

Linux 安装 MongoDB 完整指南:依赖配置、环境变量与服务启动
数据库 · 2026-09-01

Linux 安装 MongoDB 完整指南:依赖配置、环境变量与服务启动

本文详解在 Linux 系统下安装 MongoDB 的完整流程,涵盖依赖包安装、二进制包下载解压、环境变量配置、数据与日志目录创建及服务启动验证。通过标准化命令与路径说明,帮助开发者快速完成部署并确认服务状态。

MacOS安装MongoDB完整教程
数据库 · 2026-09-01

MacOS安装MongoDB完整教程

本文介绍在MacOS系统下安装MongoDB的完整流程,涵盖下载、解压、目录配置、环境变量设置及服务启动。通过明确的命令与参数说明,帮助开发者快速完成环境搭建并验证安装结果。

Ubuntu系统安装与配置Redis完整指南
数据库 · 2026-09-01

Ubuntu系统安装与配置Redis完整指南

本文详解在Ubuntu系统中安装Redis的两种主流方式:apt在线安装与源码编译安装。涵盖版本选择逻辑、服务启停与状态检查、连接验证方法,以及在线练习工具与桌面GUI客户端的对比与使用建议,帮助开发者快速搭建并验证Redis运行环境。