当前位置: 首页 > news >正文

MVCC、幻读、间隙锁与临键锁

一、MVCC 解决了什么问题?

🌱 背景:并发读写冲突

当多个事务同时操作同一行时,最经典的冲突是:

  • A 在读;

  • B 在写;

  • A 还没提交,B 改了数据;

  • 如何让 A 看到一致的结果?

MVCC(Multi-Version Concurrency Control,多版本并发控制)
👉 解决的核心问题是:

✅ 在高并发环境下,读写不阻塞,并且保证事务隔离一致性(尤其是可重复读 / Repeatable Read)。

也就是说,MVCC 让:

  • “读操作”不用加锁就能读到事务一致视图;

  • “写操作”也能正常进行,不会阻塞读;

  • 事务之间看到的数据是一致的(基于事务启动时的快照)。


二、MySQL 幻读解决了吗?

要看事务隔离级别读类型

读类型读已提交 (RC)可重复读 (RR)说明
普通快照读 (SELECT …) ✅ 幻读存在 ✅ 已解决 RR 下 MVCC + 间隙锁配合消除幻读
当前读 (SELECT … FOR UPDATE / UPDATE / DELETE) ✅ 幻读存在 ✅ 已解决 RR 下间隙锁和临键锁阻止插入

✅ 结论:

可重复读(RR) 隔离级别下,InnoDB 已经彻底解决了幻读问题。


三、MVCC 的实现原理(核心机制)

InnoDB 通过 隐藏列 + Undo Log + Read View 三个机制实现多版本并发控制。

1️⃣ 隐藏列(3个)

每行数据除了用户字段外,InnoDB 内部维护三个隐藏列:

  • DB_TRX_ID:最后修改该行的事务 ID。

  • DB_ROLL_PTR:指向 undo log 的指针(用于找到旧版本)。

  • DB_ROW_ID:行的自增主键(非显式主键时存在)。


2️⃣ Undo Log(版本链)

每当事务修改一行数据时:

  • 不直接覆盖,而是把旧值写入 undo log

  • 并在当前行的 DB_ROLL_PTR 指向该 undo log;

  • 从而形成一个“版本链”:

 
最新行 (trx_id=100) → undo_log_99 → undo_log_80 → ...

每个版本都有自己的事务 ID。


3️⃣ Read View(读视图)

当事务执行第一个一致性读时(例如 SELECT),InnoDB 会生成一个 Read View,记录以下信息:

字段含义
m_ids 当前系统中活跃事务的 ID 列表
min_trx_id 当前活跃事务中最小的事务 ID
max_trx_id 当前还未分配的下一个事务 ID(上限)
creator_trx_id 当前事务自身 ID

4️⃣ MVCC 读版本规则(判断可见性)

InnoDB 判断一行是否对当前事务可见,遵循如下规则:

设该行版本的 trx_id = X

条件是否可见
X < min_trx_id ✅ 已提交的旧事务,数据可见
X ∈ m_ids ❌ 正在执行的事务,不可见
X > max_trx_id ❌ 新事务创建的数据,不可见

如果该版本不可见,就顺着 undo log 找上一个版本,直到找到可见版本。

👉 这就是“多版本”:读事务读的是自己快照中可见版本的链


四、幻读问题 & 间隙锁 / 临键锁如何解决

1️⃣ 什么是幻读

幻读:事务 A 重复执行相同的查询,却发现多了(或少了)几行记录。

例:

 
-- 事务A SELECT * FROM user WHERE age > 18; -- 查询到10条 -- 事务B INSERT INTO user(age=20); -- 事务A 再次执行同样查询,发现11条 -> 幻读

2️⃣ MVCC 能防幻读吗?

👉 只对已存在的行有效;
但是对新插入的行(不存在的版本链)无能为力。
所以要用锁机制配合。


3️⃣ 间隙锁(Gap Lock)

作用于“记录之间的空隙”,阻止其他事务在间隙内插入数据。

例:

 
SELECT * FROM user WHERE age > 18 FOR UPDATE;

假设表中有 age=10, 20, 30
事务 A 会锁定:

 
(10,20), (20,30), (30, +∞)

→ 其他事务不能在这些区间插入新的行。

📌 解决了“幻读”的核心问题:

防止别的事务在当前事务扫描范围内插入新数据。


4️⃣ 临键锁(Next-Key Lock)

记录锁(Record Lock) + 间隙锁(Gap Lock) 的组合。
锁定“当前行 + 前一个间隙”。

例如:

 
SELECT * FROM user WHERE age = 20 FOR UPDATE;

假设有行 (age=20),临键锁会锁定区间 (10,20]

📌 优势:

  • 保证唯一性;

  • 避免插入相同主键;

  • 避免幻读(锁定扫描范围内的间隙)。


五、例子总结:快照读 vs 当前读

场景使用方式锁类型是否受MVCC影响是否防幻读
快照读 SELECT ... 无锁 ✅ 是 ✅ RR 下已解决
当前读 SELECT ... FOR UPDATEUPDATEDELETE 临键锁 / 间隙锁 ❌ 否 ✅ 防止插入幻读

六、总结对比表

机制解决问题原理是否加锁是否防幻读
MVCC 读写并发、可重复读 undo log + Read View ✅(快照读)
间隙锁 防止插入幻读 锁定记录间隙
临键锁 防止幻读 + 保证唯一性 记录锁 + 间隙锁

一句话总结:

MVCC 让读写不互斥(靠版本链解决读一致性);
间隙锁 & 临键锁让写不产生幻读(靠锁定范围防插入)。

http://www.hskmm.com/?act=detail&tid=32032

相关文章:

  • MVCC、幻读、间隙锁与临键锁(二)
  • 读AI赋能01超级能动性
  • 生物聚酯塑料回收技术创新与商业应用
  • 189 轮转数组 - MKT
  • SGD 到 AdamW 优化器的实践选型指南
  • 图文并茂展示CSS li 排版大合集,总有一款是你刚好需要的
  • 10.15 闲话
  • 函数的类型注释器
  • 如何手动构建一个线性回归模型
  • DshanPI-A1 RK3576 gmrender-resurrect B站投屏
  • 组件级异步加载与预加载策略
  • 好记性不如烂笔头之C语言优先级查询
  • SAM系列论文浅析
  • 2023 ICPC Xian
  • 2025-10-15 ?
  • 为什么一部电影,一本书一看就喜欢
  • 20251015打卡
  • 实验一 现代C++编程初体验
  • p66页2
  • 牛客119232 牛客2025秋季算法编程训练联赛1-提升组 游记
  • BroadcastChannel跨页签通信复盘总结
  • 02020510 EF Core高级10-构建动态表达式树、不推荐动态构建表达式树、动态构建IQuerable、动态构建字符串
  • 02020601 Web API01-顶级语句、全局using指令、可空类型、record类型(自动重写ToString、Equals)、init和private属性
  • OI 生涯回忆录
  • libaom 在ubuntu 上用鸿蒙OHOS编译
  • Aexlet-VGG2
  • 《膜拜!适合新手入门的卷积神经网络原理详解教程》读书报告
  • 科学与社会研讨课笔记
  • p66 实训2
  • 公众号排版用什么好?一次技术视角的系统拆解:效率、兼容与智能协同