当前位置:首页 > 文章列表 > 数据库 > MySQL > 间隙锁何时出现:用范围更新解释幻读保护边界

间隙锁何时出现:用范围更新解释幻读保护边界

来源:17golang原创 2026-10-07 15:15:54 0浏览 收藏

间隙锁最典型地出现在 InnoDB 的锁定读、UPDATE 或 DELETE 使用范围条件扫描索引时。在默认的 REPEATABLE READ 隔离级别下,InnoDB 不只锁住已经存在的索引记录,还可能保护记录之间以及范围边缘的间隙,避免别的事务插入满足同一范围的新记录。精确命中唯一索引的一条记录时,通常只需要记录锁;普通 SELECT 则依靠一致性读快照,而不是靠间隙锁解决幻读。

MySQL 8.4 官方文档:https://dev.mysql.com/doc/refman/8.4/en/innodb-locking.html

判断间隙锁不能只看 SQL 里有没有 BETWEEN。真正决定锁边界的是事务隔离级别、优化器采用的索引,以及该索引实际扫描了哪些记录和间隙。

一次看似普通的范围更新为何影响新增

下面用一个库存优先级调整的演练场景说明问题。业务事务需要把优先级 20 到 40 的待处理任务统一标记。如果这条更新长时间没有提交,另一个事务尝试新增优先级 30 的任务时会等待。表中原有行是否正好存在 priority = 30 并不是关键;30 落在受保护的索引间隙内,就足以让插入等待。

-- 建立演练表,并让范围条件可以沿二级索引扫描
CREATE TABLE task_queue (
    id BIGINT PRIMARY KEY,
    priority INT NOT NULL,
    state VARCHAR(16) NOT NULL,
    KEY idx_priority (priority)
) ENGINE = InnoDB;

-- 只准备范围两侧和范围内的少量记录,便于观察间隙
INSERT INTO task_queue (id, priority, state) VALUES
    (1, 10, 'pending'),
    (2, 20, 'pending'),
    (3, 40, 'pending'),
    (4, 50, 'pending');

影响面应当表述为“与 idx_priority 扫描区间重叠的写入可能等待”,而不是“整张表被锁”。同一张表上不相交的索引区间仍可能继续并发,但是否冲突要以实际执行计划和锁信息为准。

用双事务时间线还原等待现场

先在会话 A 中使用默认的 REPEATABLE READ 开启事务并执行范围更新,暂时不提交:

-- 会话 A:显式使用 RR,锁住范围更新扫描到的索引区间
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;

-- 范围 UPDATE 会对匹配记录加排他锁,并保护相关索引间隙
UPDATE task_queue
SET state = 'reviewing'
WHERE priority BETWEEN 20 AND 40;

-- 演练期间先不要提交,留出观察并发等待的窗口

再在会话 B 中插入一个落在区间内的新优先级:

-- 会话 B:30 位于 20 与 40 之间,会与受保护间隙发生冲突
START TRANSACTION;
INSERT INTO task_queue (id, priority, state)
VALUES (5, 30, 'pending');

-- 等待解除后再按演练目标提交或回滚,避免遗留长事务

此时会话 B 的插入可能进入锁等待。会话 A 提交或回滚后,等待才有机会继续。这里不是已有的 priority = 30 行被锁住,而是新索引记录准备插入的位置落在范围更新所保护的间隙里。

三个条件共同决定间隙锁是否出现

第一看隔离级别。MySQL 8.4 官方手册说明,InnoDB 默认使用 REPEATABLE READ;对锁定读、UPDATE 和 DELETE,除唯一索引唯一条件外,通常会对扫描的索引范围采用间隙锁或 next-key 锁。切换到 READ COMMITTED 后,搜索和索引扫描的间隙锁通常被关闭,只在外键约束检查和重复键检查等场景保留。

第二看访问路径。条件列有合适索引时,锁边界通常围绕该索引的扫描范围;没有可用索引时,InnoDB 可能扫描并锁住更多聚簇索引记录,使并发影响远大于业务条件表面上描述的范围。因此,先看执行计划,再讨论“锁了几行”。

-- 用 EXPLAIN 确认优化器是否选择 idx_priority 以及估算扫描范围
EXPLAIN
UPDATE task_queue
SET state = 'reviewing'
WHERE priority BETWEEN 20 AND 40;

第三看条件是否是唯一索引的精确等值。完整唯一键命中单条记录时,InnoDB 通常只锁索引记录,不需要保护前方间隙;普通索引等值、只使用联合唯一索引的一部分列,或任何范围搜索,都不能直接套用这个例外。

范围更新中的二级索引记录、间隙与并发插入位置静态关系图
图1:范围更新锁边界说明图。重点看 priority 20、40 两条索引记录之间的间隙,以及 priority 30 的插入位置;这是静态结构图,不是数据库运行截图。

根因不是 BETWEEN,而是扫描到的索引区间

很多排障记录把原因简化成“BETWEEN 会产生间隙锁”,这个说法容易误导。>、、前缀范围、普通索引等值乃至缺少合适索引的条件,都可能形成范围型扫描。反过来,即使 SQL 看起来复杂,只要最终使用完整唯一索引精确定位一条记录,也可能只产生记录锁。

在 InnoDB 中,next-key 锁可以理解为“某条索引记录的记录锁 + 该记录前方的间隙锁”。它保护的是索引中的位置范围,使另一个事务不能在受保护区间内插入新的索引记录。纯间隙锁本身主要抑制插入;它不是对现有记录内容的排他修改许可,也不要把它等同于表锁。

还要把普通 SELECT 与锁定读分开。默认 RR 下的普通查询通常是一致性非锁定读,通过同一事务的读视图获得稳定结果;SELECT ... FOR UPDATE、SELECT ... FOR SHARE、UPDATE 和 DELETE 才进入锁定访问路径。把两类读混在一起,是判断“为什么有时没有间隙锁”的常见误区。

RR与RC隔离级别、索引条件和锁类型边界静态关系图
图2:隔离级别与索引条件的锁类型边界说明图。RR 范围扫描、RR 唯一键精确等值、RC 范围扫描及其例外被分组展示;这是静态结构图,不代表实时锁状态。

缩小锁范围的修复动作

先补正确索引。让过滤条件沿选择性合适的索引缩小扫描范围,是最直接的改进。索引设计应服务真实谓词和排序,不要为了“消除间隙锁”盲目创建重复索引。

让条件尽量精确。能够用主键或完整唯一键更新单条记录时,不要先宽范围查询再逐条修改。对于批处理,可按稳定主键分段并及时提交,减少一次事务覆盖的索引区间和持锁时间。

缩短事务生命周期。不要在事务内等待网络调用、人工确认或长时间计算。应用异常路径必须回滚,连接池归还连接前应确保事务已经结束。

按业务一致性要求选择隔离级别。READ COMMITTED 能减少搜索和索引扫描中的间隙锁,但允许并发插入形成新的范围行,并且每次一致性读都会建立新快照。它不是通用“解锁开关”,只能在业务接受相应语义后采用。

场景典型锁边界优先检查
RR + 唯一索引完整等值通常是目标索引记录条件是否覆盖完整唯一键
RR + 普通索引范围扫描记录及相关间隙执行计划与扫描区间
RR + 缺少合适索引可能扩大到大量聚簇索引记录与间隙是否需要复合索引
RC + 搜索或索引扫描主要锁记录,通常不锁搜索间隙外键、重复键检查等例外

把防复发检查写进上线清单

复查时不要只盯着 SQL 文本。先用 EXPLAIN 确认访问路径,再在演练环境用两个短事务复现冲突,并通过 Performance Schema 查看持有锁和等待关系。MySQL 8.4 中,performance_schema.data_locks 记录锁信息,data_lock_waits 用来关联阻塞锁与请求锁。

-- 查看当前 InnoDB 锁的对象、索引、模式和锁数据
SELECT ENGINE_TRANSACTION_ID,
       OBJECT_SCHEMA,
       OBJECT_NAME,
       INDEX_NAME,
       LOCK_TYPE,
       LOCK_MODE,
       LOCK_STATUS,
       LOCK_DATA
FROM performance_schema.data_locks
WHERE OBJECT_SCHEMA = DATABASE()
  AND OBJECT_NAME = 'task_queue';

-- 查看谁正在阻塞谁,便于把等待事务和持锁事务对应起来
SELECT REQUESTING_ENGINE_TRANSACTION_ID,
       BLOCKING_ENGINE_TRANSACTION_ID,
       REQUESTING_ENGINE_LOCK_ID,
       BLOCKING_ENGINE_LOCK_ID
FROM performance_schema.data_lock_waits;

上线前至少回答四个问题:范围条件走哪个索引;最坏情况下扫描多少索引记录;事务最长持锁多久;区间内插入是必须阻止还是可以接受。把这些答案写进变更说明,比记住“RR 有间隙锁”更能减少下一次并发故障。

常见疑问

没有命中任何行,还会有间隙锁吗?

可能会。锁定范围搜索即使没有找到现有记录,也可能保护搜索落点附近的索引间隙,从而阻止其他事务插入会落入该范围的新记录。

普通 SELECT 为什么重复执行也看不到新插入行?

默认 RR 下,普通 SELECT 通常使用一致性非锁定读,同一事务读取既有快照。结果稳定来自 MVCC 读视图,不应据此推断查询拿到了间隙锁。

改成 READ COMMITTED 就一定不会等待吗?

不会。RC 主要减少搜索和索引扫描的间隙锁,记录锁冲突、唯一键冲突、外键检查和长事务仍可造成等待。隔离级别只能改变一部分锁策略,不能替代索引和事务治理。

如何确认锁住的是哪个索引?

先用 EXPLAIN 查看计划中的索引,再结合 performance_schema.data_locks 的 INDEX_NAME、LOCK_MODE 和 LOCK_DATA 观察。两者结合,才能把业务条件、扫描路径和实际锁对象对应起来。

参考资料:MySQL 8.4 Reference Manual:https://dev.mysql.com/doc/refman/8.4/en/innodb-locking.html、https://dev.mysql.com/doc/refman/8.4/en/innodb-transaction-isolation-levels.html、https://dev.mysql.com/doc/refman/8.4/en/innodb-information-schema-understanding-innodb-locking.html

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
珊瑚粉与沙金渐变沙丘的极简手机壁纸提示词珊瑚粉与沙金渐变沙丘的极简手机壁纸提示词
上一篇
珊瑚粉与沙金渐变沙丘的极简手机壁纸提示词
errors.Is 与直接比较为何结果不同,错误链如何遍历
下一篇
errors.Is 与直接比较为何结果不同,错误链如何遍历
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    365次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    421次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    435次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    387次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    214次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码