当前位置:首页 > 文章列表 > 数据库 > MySQL > MySQL InnoDB 隔离级别并发更新为什么出现间隙锁等待为什么结果为空或不符合预期

MySQL InnoDB 隔离级别并发更新为什么出现间隙锁等待为什么结果为空或不符合预期

来源:17golang原创 2026-11-10 00:14:29 0浏览 收藏

InnoDB 并发 UPDATE 等待不一定是两个事务修改同一条记录。REPEATABLE READ 下范围锁定可能保护索引记录之间的间隙;另一事务修改索引列时,新的索引项需要进入该间隙,也可能等待。纯间隙锁彼此可以共存,真正需要辨别的是记录冲突、插入意向请求与间隙保护的关系。

我会先查请求锁落在哪个索引,再考虑调整隔离级别。看到 UPDATE 或空结果就归因于“整张表锁住”,会把解决方向带偏。下面按 MySQL 8.4 文档组织受控示例,没有在本机执行,不提供虚构的锁输出。

官方地址:https://dev.mysql.com/doc/refman/8.4/en/innodb-locking.html

先辨别记录锁与间隙约束

记录锁针对索引记录,间隙锁抑制往受保护区间插入,next-key 锁包含记录与其前方间隙。它们的对象是索引结构,不能只看 SQL 返回多少业务行来推断锁范围。

完整唯一键等值命中已有记录,与范围条件不是同一场景;不存在的键、联合唯一索引只指定部分列,以及非唯一索引条件,都不能套用同一种锁结论。普通一致性读的快照结果也不能说明锁定读或 UPDATE 没有等待。

范围条件关联二级索引,间隙保护与待插入索引项属于不同锁对象
范围条件关联二级索引,间隙保护与待插入索引项属于不同锁对象;静态结构说明图,不是锁等待截图或实测结果。

用两个会话理解索引值迁移

使用独立练习库和两个连接。示例显式指定隔离级别,并为讲解固定 idx_score 访问路径;FORCE INDEX 是演示条件,不是生产优化建议。

-- 仅在独立练习库创建,不改动业务表。
CREATE TABLE gap_update_demo (
  id INT NOT NULL PRIMARY KEY,
  score INT NOT NULL,
  note VARCHAR(40) NOT NULL,
  KEY idx_score (score)
) ENGINE=InnoDB;
INSERT INTO gap_update_demo VALUES (1,10,'甲'),(2,30,'乙');
-- 会话A:事务保持打开以观察范围保护,结束后必须回滚。
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
SELECT id,score FROM gap_update_demo FORCE INDEX (idx_score)
WHERE score>10 AND score

score 在10和30之间没有现存行,因此 A 可以返回空结果;这不代表该索引间隙没有被保护。

-- 会话B:修改另一条记录的索引值,目标值位于A保护的间隙。
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
UPDATE gap_update_demo SET score=20 WHERE id=1;
-- 此语句可能等待;完成或超时后均应明确执行 ROLLBACK。

B 按主键选择 id=1,但 score 改为20还涉及维护 idx_score。新索引项位于 A 的范围保护内,所以应关注插入意向等待,而不能只比较两条语句是否返回同一 id。具体锁标记与附加锁以实际环境为准。

若只改未参与该索引的 note,涉及的索引维护不同,不能拿它证明修改 score 也不会等待。真正生产语句往往还有外键、唯一性约束和其他索引,需要将它们一起纳入分析。

关联等待事务与索引对象

-- 在有监控表读取权限的第三个会话执行,保留实际返回而非猜测锁类型。
SELECT w.REQUESTING_ENGINE_TRANSACTION_ID AS waiting_trx,
       w.BLOCKING_ENGINE_TRANSACTION_ID AS blocking_trx,
       r.OBJECT_SCHEMA,r.OBJECT_NAME,r.INDEX_NAME,
       r.LOCK_TYPE,r.LOCK_MODE AS requested_mode,
       r.LOCK_STATUS,r.LOCK_DATA,
       b.LOCK_MODE AS blocking_mode
FROM performance_schema.data_lock_waits AS w
JOIN performance_schema.data_locks AS r
  ON r.ENGINE=w.ENGINE
 AND r.ENGINE_LOCK_ID=w.REQUESTING_ENGINE_LOCK_ID
JOIN performance_schema.data_locks AS b
  ON b.ENGINE=w.ENGINE
 AND b.ENGINE_LOCK_ID=w.BLOCKING_ENGINE_LOCK_ID
WHERE r.OBJECT_NAME='gap_update_demo';

data_lock_waits 记录请求与阻塞锁的关联,data_locks 提供表、索引和锁模式。关联时同时使用 ENGINE 和锁ID。查询结果是当前观测,不是历史永久记录;等待结束后为空很正常,权限或对象过滤错误也要分开处理。

等待关联表通过请求锁ID和阻塞锁ID连接两份锁明细
等待关联表通过请求锁ID和阻塞锁ID连接两份锁明细;静态结构说明图,不是锁等待截图或实测结果。

不能仅凭 LOCK_MODE 含 X 就判断是同一行写冲突,也不能见到 GAP 就断言两个纯间隙锁互相排斥。要结合 INDEX_NAME、LOCK_DATA、请求类型、阻塞关系和事务里的前序语句。

比较改索引和改隔离级别的代价

改动方向可能收益需要承担的代价
缩小扫描范围减少涉及的索引区域索引维护与计划变化
缩短事务缩短锁持有时间调整业务事务边界
评估 READ COMMITTED减少搜索扫描的间隙保护重新评估一致性与幻读
统一访问顺序降低交叉等待风险多入口共同遵守

READ COMMITTED 通常不为搜索和索引扫描使用间隙锁,但外键检查和重复键检查仍有例外;记录写冲突也不会消失。改变隔离级别属于业务语义选择,不是清除所有等待的开关。

我倾向先看事务有没有把远程调用、用户交互或大批量循环包在持锁区间,再决定是否改变隔离策略。只把超时调大,可能只是让排队更久。也不要用全局隔离级别改动替代对某个连接或任务的评估。

收尾事务并定义重试

观察完在 A 执行 ROLLBACK,让 B 从等待中结束;随后在 B 也执行 ROLLBACK,明确释放事务。若 B 已因超时退出,仍应由应用明确结束整笔事务,不要以为报错后连接里的所有状态都自动复原。

业务重试要有次数与退避上限,并基于幂等任务或唯一业务键。等待超时、死锁和语句成功但受影响行数为零是不同状态;“无结果”不能代替对错误码、提交结果与过滤条件的判断。

查询没有返回行,为什么还能挡住更新?

受保护的是符合范围的索引间隙,空业务结果并不能排除范围保护。

两个间隙锁一定冲突吗?

不一定,纯间隙锁可以共存。需要进入间隙的索引插入请求则可能被抑制。

主键 UPDATE 为何还涉及二级索引?

定位记录的路径与修改后需要维护的索引不是同一回事。更新索引列要处理新旧索引项。

参考: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/performance-schema-data-lock-waits-table.html。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
MySQL InnoDB 隔离级别并发更新为什么出现间隙锁等待怎样完成一个可复用的最小示例MySQL InnoDB 隔离级别并发更新为什么出现间隙锁等待怎样完成一个可复用的最小示例
上一篇
MySQL InnoDB 隔离级别并发更新为什么出现间隙锁等待怎样完成一个可复用的最小示例
下一篇
下一篇
暂无
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之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模型性能。
    965次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    965次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    1077次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    1055次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    885次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码