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

MVCC、幻读、间隙锁与临键锁(三)

一、MVCC解决了什么问题?

MVCC 解决了数据库高并发场景下的两大核心问题:

  1. 读写阻塞:在传统的锁机制下,读操作可能会阻塞写操作,写操作也一定会阻塞读操作。当有大量读写操作并发时,数据库性能会急剧下降。

  2. 事务隔离的代价:为了实现高级别的事务隔离(如可重复读),需要对数据加锁,这严重影响了并发性能。

MVCC的核心理念是:通过创建数据快照,让读操作和写操作互不阻塞。读操作访问事务开始时的快照版本,写操作则创建新版本。 这使得在 REPEATABLE READ 隔离级别下,读操作无需加锁,极大地提升了数据库的并发处理能力。


二、MVCC的原理是什么?

MVCC的实现依赖于三个核心要素:

  1. 隐式字段:

    • DB_TRX_ID:一个6字节的字段,表示最后一个对本行数据做修改的事务ID。

    • DB_ROLL_PTR:一个7字节的字段,指向回滚段中的Undo Log记录,也就是指向该行数据的上一个历史版本。

    • DB_ROW_ID:一个6字节的隐藏自增ID,如果表没有主键,InnoDB会基于此生成聚簇索引。

  2. Undo Log:

    • 当事务修改数据时,会将数据的旧版本拷贝到Undo Log中。

    • 这个Undo Log通过DB_ROLL_PTR指针形成一个数据版本链。如上图所示,最新的数据V2指向旧版本V1。读请求根据规则遍历这个链,找到适合自己的数据版本。

  3. Read View:

    • 这是事务进行快照读操作时产生的读视图。在事务执行快照读的那一刻,会生成数据库系统当前的一个快照。

    • Read View主要用于做可见性判断,即决定当前事务能看到哪个版本的数据。

    • 它主要包含:

      • m_ids:生成Read View时,系统中活跃的(未提交的)事务ID列表。

      • min_trx_id:活跃事务列表中的最小事务ID。

      • max_trx_id:生成Read View时,系统应该分配给下一个事务的ID。

      • creator_trx_id:创建该Read View的事务ID。

可见性判断规则:
沿着数据版本链,从最新版本开始检查,如果数据行的DB_TRX_ID

  • 等于creator_trx_id? -> 可见。说明是本事务自己修改的。

  • 小于min_trx_id? -> 可见。说明该版本在本次Read View创建前已提交。

  • 大于等于max_trx_id? -> 不可见。说明该版本在本次Read View创建后才生成。

  • min_trx_idmax_trx_id之间,但不在m_ids中? -> 可见。说明该版本对应的事务已提交。

  • min_trx_idmax_trx_id之间,且在m_ids中? -> 不可见。说明该版本对应的事务仍活跃,未提交。

如果不可见,就顺着DB_ROLL_PTR指针找到上一个版本,重复上述判断,直到找到可见的数据版本。


三、MySQL幻读解决了吗?

这是一个非常容易混淆的问题。结论是:

  • 在MySQL的InnoDB引擎的 REPEATABLE READ 隔离级别下,通过 MVCC + Next-Key Lock 的机制,已经解决了幻读问题。

什么是幻读?
一个事务在重新执行同一个查询时,返回了更多的行或者发现了之前不存在的行(由于其他事务的插入或删除操作)。

如何解决的?

  1. 对于快照读:普通的 SELECT ... 语句使用MVCC。因为事务始终读取的是它开始时的快照,所以其他事务的插入/删除操作对该事务是不可见的,从而防止了幻读。

  2. 对于当前读:像 SELECT ... FOR UPDATE / SELECT ... LOCK IN SHARE MODE / UPDATE / DELETE 这类操作,它们读取的是数据的最新版本,并且需要加锁。这时,MVCC无法防止幻读。InnoDB通过引入 Next-Key Lock 来给扫描到的索引范围加锁,阻止其他事务在锁定范围内插入新数据,从而解决了当前读下的幻读问题。

所以,标准的MySQL InnoDB在 REPEATABLE READ 级别下,已经可以完全防止幻读。


四、间隙锁和临键锁解决了什么问题?原理是什么?

它们共同解决了 "当前读" 场景下的幻读问题。

1. 间隙锁

  • 解决的问题:防止在某个索引记录的间隙中插入新数据,从而解决幻读。

  • 锁定的目标:锁定一个索引记录之间的开区间。例如,表中存在id为1和5的记录,那么间隙锁可能锁定区间 (1, 5)

  • 原理:当一个事务执行 SELECT * FROM table WHERE id BETWEEN 1 AND 10 FOR UPDATE 时,InnoDB不仅会锁住id=1和10的记录(如果存在),还会锁住(1,10)这个区间内的所有"间隙"。这阻止了任何其他事务在(1,10)的范围内插入新的id值,比如插入id=3或id=7,从而防止了幻读。

2. 临键锁

  • 本质:临键锁是 记录锁 和 间隙锁 的结合。

  • 锁定的目标:它会锁定索引记录本身以及该记录之前的间隙。

    • 例如,如果数据库中有索引记录10, 11, 13。

    • 那么临键锁可能锁定的区间是:(-∞, 10](10, 11](11, 13](13, +∞]

  • 原理:当执行 SELECT * FROM table WHERE id > 10 FOR UPDATE 时,InnoDB会从找到的第一个大于10的记录(比如11)开始,对其加临键锁(锁住(10, 11]),然后继续向后加锁,直到最后一个记录,最后还会对最后一个记录之后的间隙(比如(13, +∞))加一个间隙锁。这样,任何想在锁定范围内插入新数据的操作都会被阻塞。

💎 总结

 
机制核心解决问题原理
MVCC 快照读的读写阻塞与不可重复读 通过Undo Log版本链和Read View实现多版本数据快照,读操作无需加锁。
间隙锁 当前读的幻读(防止插入) 锁定索引记录之间的空隙,阻止其他事务在范围内插入新数据。
临键锁 当前读的幻读(防止插入和修改) 记录锁 + 间隙锁,锁定记录本身及之前的间隙,是InnoDB默认的行锁算法。

它们协同工作,使得MySQL InnoDB在 REPEATABLE READ 隔离级别下,既保证了高并发性能(通过MVCC),又实现了严格的事务隔离,解决了脏读、不可重复读和幻读所有问题。

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

相关文章:

  • MVCC、幻读、间隙锁与临键锁
  • 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