news 2026/6/25 20:40:01

幻读与 Next-Key Lock:可重复读隔离级别如何解决幻读

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
幻读与 Next-Key Lock:可重复读隔离级别如何解决幻读

在上一篇中,我们全面梳理了 InnoDB 的锁分类,认识了记录锁、间隙锁、临键锁和插入意向锁。其中临键锁(Next-Key Lock)被反复提及——它是 InnoDB 在REPEATABLE READ隔离级别下默认的行锁形式,也是防止幻读的核心武器。

本文将聚焦于幻读这一特殊的并发问题,深入剖析:

  • 幻读的严格定义与场景再现
  • Next-Key Lock 的工作原理
  • 为什么 RR 级别能通过它防止大部分幻读
  • 幻读与 Serializable 隔离级别的对比
  • 实战:演示幻读的发生与 Next-Key Lock 的阻止效果

读完本文,你将能清晰解释“InnoDB 默认隔离级别为什么能防幻读”,并理解其实现机制。


1. 再识幻读:同一个事务,不同的结果集

回顾一下三类并发问题:

  • 脏读:读到未提交的数据。
  • 不可重复读:同一行被修改并提交,两次读的值不同。
  • 幻读:同一范围查询,两次返回的行数不同。

幻读的“幻”在于,第二次查询时莫名其妙多出了几行(或少了),就像出现了幻觉。它通常由其他事务的INSERTDELETE引起,不是修改已有行,而是改变结果集的大小

典型场景

  • 事务 T1 查询“所有余额大于 500 的账户”,返回 3 行。
  • 事务 T2 插入一行余额为 600 的新账户,并提交。
  • 事务 T1 再次执行同一查询,返回 4 行。

如果 T1 基于第一次查询的结果做了汇总计算(比如 SUM),就会发现两次总和不一致。这种不一致可能导致业务逻辑出错。


2. Next-Key Lock:行锁 + 间隙锁的合体

2.1 行锁为什么不够?

普通的记录锁只锁定已存在的行。如果 T1 对balance > 500的所有现有行加了行锁,T2 仍然可以插入一条新的balance = 600的记录——因为这条记录还不存在,没有任何锁阻止它。于是幻读依然发生。

要阻止插入,必须锁住行与行之间的“间隙”。这就是间隙锁(Gap Lock)的作用。

2.2 Next-Key Lock = Record Lock + Gap Lock

Next-Key Lock 锁定的是一个左开右闭的区间(a, b],包含:

  • 对该区间内已有记录的记录锁(防修改)
  • 对这些记录之间间隙的间隙锁(防插入)

它相当于在索引上“画地为牢”,把范围查询锁住的每一段间隙都保护起来。

示例:假设表中有索引记录10,20,30。执行:

SELECT*FROMtWHEREidBETWEEN15AND25FORUPDATE;

InnoDB 会加以下 Next-Key Locks:

  • (10, 20]区间(覆盖了 15~20)
  • (20, 30]区间(覆盖了 20~25)

此外,还会加上间隙锁(20, 25)以及可能的一些额外锁,实际上会锁住(10, 20](20, 30],有效阻止其他事务在(10, 30)间插入新行,以及修改2030

2.3 如何防止幻读?

在上述例子中,如果 T2 试图插入id = 18id = 25,插入意向锁会试图在(10, 20)(20, 30)间隙上加锁,但这些间隙已被 T1 的 Next-Key Lock 保护,插入意向锁会被阻塞。因此 T2 无法在 T1 的两次查询之间插入新行,幻读就此被阻止。


3. 隔离级别与 Next-Key Lock 的关系

  • READ COMMITTED:不使用间隙锁,只使用记录锁。因此无法防止幻读。
  • REPEATABLE READ:默认使用 Next-Key Lock,防止幻读。
  • SERIALIZABLE:最严格,所有读操作都隐式加共享锁(类似SELECT ... FOR SHARE),所有读写完全串行化,自然也防幻读。

InnoDB 的 RR 通过 Next-Key Lock在大部分场景下阻止了幻读,但并非绝对。某些边缘情况(如先快照读后当前读,或者不同索引条件)仍可能出现类似幻读的现象。这点在后续 MVCC 篇会结合 ReadView 深入探讨。

Serializable 与 RR 的区别

  • Serializable 强制所有读取都加锁,导致读读也可能阻塞(如果使用 FOR UPDATE 的语义),并发度极低。
  • RR 下的普通SELECT是无锁的快照读(通过 MVCC),只有显式加锁的SELECT ... FOR UPDATE/SHARE或 DML 操作才会使用 Next-Key Lock。因此并发度远高于 Serializable。

4. 实战:演示幻读与 Next-Key Lock 的阻止

我们来实际观察 RC 下的幻读现象,以及 RR 下 Next-Key Lock 如何防幻读。

4.1 准备测试表

USElibrary_db;CREATETABLEphantom_test(idINTPRIMARYKEY,nameVARCHAR(20))ENGINE=InnoDB;INSERTINTOphantom_testVALUES(10,'A'),(20,'B'),(30,'C');

4.2 READ COMMITTED 下的幻读

会话 A

SETSESSIONTRANSACTIONISOLATIONLEVELREADCOMMITTED;STARTTRANSACTION;-- 第一次查询SELECT*FROMphantom_testWHEREidBETWEEN15AND25;-- 返回:Empty set(无记录)

会话 B

INSERTINTOphantom_testVALUES(18,'phantom');COMMIT;

会话 A(继续同一事务):

-- 第二次查询SELECT*FROMphantom_testWHEREidBETWEEN15AND25;-- 返回:id=18(幻读!)COMMIT;

因为 RC 下没有间隙锁,B 插入成功,A 看到了新行。

4.3 REPEATABLE READ 下 Next-Key Lock 的阻止

会话 A

SETSESSIONTRANSACTIONISOLATIONLEVELREPEATABLEREAD;STARTTRANSACTION;SELECT*FROMphantom_testWHEREidBETWEEN15AND25FORUPDATE;-- 加锁区间(10,20] 和 (20,30]

会话 B

INSERTINTOphantom_testVALUES(18,'blocked');-- 会阻塞!因为 18 落在 (10,20) 间隙中

此时会话 B 会等待锁,直到会话 A 提交或回滚。如果等待超时,会报错:

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

会话 A

COMMIT;

会话 B 会在 A 提交后立即插入成功。这完美展示了 Next-Key Lock 对幻读的阻止效果。

4.4 查看锁信息

在阻塞期间,可以在第三个会话中查看锁情况:

SELECTlock_type,lock_mode,lock_data,lock_statusFROMperformance_schema.data_locksWHEREobject_name='phantom_test';

你会看到X,GAPX等锁模式,以及被锁定的索引记录和间隙。

4.5 清理

DROPTABLEphantom_test;

5. Next-Key Lock 的局限与代价

虽然 Next-Key Lock 很有力,但并非完美:

  • 锁范围可能扩大:如果 WHERE 条件无法使用精确索引,导致扫描全表,Next-Key Lock 会锁住整个索引的所有间隙和记录,接近于表锁。
  • 影响并发插入:被锁住的间隙内,其他事务的 INSERT 会被阻塞,可能导致写入性能下降。
  • 死锁风险增加:多个事务各自持有一些间隙锁,又等待对方释放,形成死锁。

因此,在业务层设计查询时,要尽量确保 WHERE 条件能走合适的索引,避免大范围扫描导致的锁膨胀。


6. 小结

本文围绕幻读与 Next-Key Lock 进行了深入探讨:

  • 幻读:同一事务内两次范围查询结果集的行数变化,由其他事务插入/删除引起。
  • Next-Key Lock= 记录锁 + 间隙锁,锁定左开右闭区间,是 InnoDB 在 RR 级别防止幻读的核心机制。
  • RC vs RR:RC 无间隙锁,存在幻读;RR 使用 Next-Key Lock 阻止插入,实现防幻读。
  • Serializable:通过强制读锁将并发度降到最低,完全防幻读,但代价巨大。
  • 实战:亲手在 RC 下再现幻读,在 RR 下用 Next-Key Lock 成功阻止。

通过本文,你应该对“InnoDB 如何解决幻读”有了直观且深入的理解。下一篇我们将进入另一个并发难题——死锁的产生、检测与避免,学习如何从日志中诊断死锁,以及如何通过设计规范避免它。

思考题

  1. 为什么在 RC 隔离级别下UPDATE也可能引发幻读?举个例子。
  2. 如果phantom_test表上没有索引,SELECT ... FOR UPDATE会加什么锁?
  3. 尝试修改事务隔离级别为SERIALIZABLE,执行相同的测试,观察锁等待现象。

参考资料

  • MySQL 8.0 Reference Manual - InnoDB Next-Key Locking
  • MySQL 8.0 Reference Manual - Phantom Rows
  • MySQL 8.0 Reference Manual - Gap Locks

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 6:14:12

在线手机号测吉凶查询,913.com.cn简单好操作

手机号码使用得越频繁、持续时间越久,其所承载的数字能量就会逐步累积,磁场强度也会随之提升。而这种不断增强的能量,对个人各方面运势的作用力也会越来越明显。 那么哪些号码越用能量越强?它们具备什么特征? 越用能…

作者头像 李华
网站建设 2026/6/10 9:21:00

如何免费解锁Steam创意工坊:跨平台游戏模组终极下载指南

如何免费解锁Steam创意工坊:跨平台游戏模组终极下载指南 【免费下载链接】WorkshopDL WorkshopDL - The Best Steam Workshop Downloader 项目地址: https://gitcode.com/gh_mirrors/wo/WorkshopDL 还在为无法下载Steam创意工坊模组而烦恼吗?想象…

作者头像 李华
网站建设 2026/6/10 10:08:20

RTL8201FI-VB-CG,支持 MII/RMII 接口的 10/100M 千兆级以太网 PHY

型号介绍RTL8201FI-VC-CG 是 瑞昱(Realtek)推出的单端口 10/100M 以太网 PHY 收发芯片,采用 0.11m 先进 CMOS 工艺打造。芯片采用双电压架构,数字 IO、模拟电路使用 3.3V 供电,内核工作电压为 1.1V,内置线性…

作者头像 李华
网站建设 2026/6/10 10:08:45

ACR-PINN:解决物理信息神经网络梯度冲突的新方法

1. ACR-PINN:物理信息神经网络的梯度冲突优化新范式在科学计算领域,物理信息神经网络(Physics-Informed Neural Networks, PINN)近年来展现出巨大潜力。这种将偏微分方程(PDE)物理约束直接融入神经网络训练…

作者头像 李华
网站建设 2026/6/11 1:06:40

VRCT:5步实现VRChat跨语言无障碍交流的AI神器

VRCT:5步实现VRChat跨语言无障碍交流的AI神器 【免费下载链接】VRCT VRCT(VRChat Chatbox Translator & Transcription) 项目地址: https://gitcode.com/gh_mirrors/vr/VRCT **VRCT(VRChat Chatbox Translator & Transcription&#xff0…

作者头像 李华
网站建设 2026/6/10 22:25:21

繁易HMI快速上手工程包:含60吨纯水系统完整界面与配置

本文还有配套的精品资源,点击获取 简介:直接可用的繁易HMI触摸屏工程模板,基于fshmi平台构建,开箱即加载运行。核心是江苏鸿顺60吨纯水系统的.fsprj工程文件,已集成标准界面布局、统一配色方案、自定义软键盘、历史…

作者头像 李华