MySQL InnoDB 隔离级别并发更新为什么出现间隙锁等待为什么结果为空或不符合预期
来源:17golang原创
时间:2026-11-10 00:14:29 436浏览 收藏
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。查询结果是当前观测,不是历史永久记录;等待结束后为空很正常,权限或对象过滤错误也要分开处理。

不能仅凭 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。
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
数据库 · MySQL | 21分钟前 | MySQL · InnoDB · 数据库排查 · mysql innodb 隔离级别 间隙锁 READ COMMITTED REPEATABLE READ next-key lock141 收藏
-
285 收藏
-
数据库 · MySQL | 42分钟前 | MySQL · 错误处理 · 存储过程 · 数据库开发 · mysql 存储过程 signal 业务错误 SQLSTATE MYSQL_ERRNO MESSAGE_TEXT404 收藏
-
367 收藏
-
400 收藏
-
492 收藏
-
217 收藏
-
395 收藏
-
273 收藏
-
376 收藏
-
423 收藏
-
412 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习