【Java 开发日记】我们来讲一讲 MVCC 的实现原理

【Java 开发日记】我们来讲一讲 MVCC 的实现原理

目录

前言

一、MVCC 要解决的核心问题

二、MVCC 的实现基石

1. 隐藏字段

2. Undo Log

3. Read View(读视图)

三、可见性算法:如何判断一个版本是否可见?

四、在不同隔离级别下的表现

1. REPEATABLE READ(可重复读 - MySQL 默认级别)

2. READ COMMITTED(读已提交)

五、总结与流程图

六、补充说明


前言

MVCC,全称 Multi-Version Concurrency Control,即多版本并发控制。它是一种为了提高数据库并发性能而提出的技术,使得在并发读写数据库时,读操作不会阻塞写操作,写操作也不会阻塞读操作。这就解决了传统的锁机制带来的性能瓶颈问题。

MySQL 中,InnoDB 存储引擎 实现了 MVCC。

一、MVCC 要解决的核心问题

在没有 MVCC 的情况下,如果我们要保证事务的隔离性(例如可重复读级别),通常会用锁来实现。当一个事务正在读取某些数据时,其他事务就不能修改这些数据(共享锁),这会导致“读-写”冲突;同样,一个事务在修改数据时(排他锁),其他事务也不能读取,这会导致“写-读”冲突。MVCC 通过创建数据的历史版本来优雅地解决这个问题。

核心思想: 为每行数据维护多个历史版本。当一个事务需要读取数据时,它会看到在它开始之前就已经提交的某个一致性数据快照,而不管当前这些数据被其他事务修改成了什么样子。

二、MVCC 的实现基石

MVCC 的实现依赖于三个核心组件:

  1. 隐藏字段
  2. Undo Log
  3. Read View

下面我们逐一详解。

1. 隐藏字段

InnoDB 为每一行数据(记录)都添加了三个系统隐藏字段:

  • DB_TRX_ID (6字节):事务ID。表示最后一次插入或更新该行的事务ID。此外,删除在内部也被视为更新,会在该行数据中设置一个特殊的删除标记。
  • DB_ROLL_PTR (7字节):回滚指针。指向该行数据的上一个历史版本,存储在 Undo Log 中。
  • DB_ROW_ID (6字节):行ID。随着新行插入而单调递增的行ID。如果表没有定义主键,InnoDB 会基于这个字段生成一个聚簇索引。

注意: 实际上还有一个删除标记的隐藏字段,用于标记该行是否被删除。

2. Undo Log

Undo Log(回滚日志)主要有两个作用:

  1. 事务回滚时,用于恢复数据。
  2. 实现 MVCC 的关键。它存储了数据行的历史版本。

工作原理:
当一个事务对某行数据进行修改(INSERT, UPDATE, DELETE)时:

  • UPDATE / DELETE: 会先将该行数据的当前版本(修改前)复制到 Undo Log 中。这个副本中包含了 DB_TRX_IDDB_ROLL_PTR。新的 DB_ROLL_PTR 会指向这个刚刚存入 Undo Log 的旧版本。然后才在表中修改该行数据,写入新的 DB_TRX_ID 和新的 DB_ROLL_PTR
  • INSERT: 因为新插入的数据对之前的事务不可见,所以它的 Undo Log 只在事务回滚时需要,在 MVCC 中作用不大。

因此,通过 DB_ROLL_PTR 指针,一行数据的所有历史版本(快照)被串联成一个链表,这个链表就存放在 Undo Log 中。这个链表称为 版本链。链表的头节点是当前的最新记录。

3. Read View(读视图)

Read View 是事务在进行快照读操作时产生的。它定义了当前事务在执行期间,能看到哪些版本的数据。

Read View 主要包含以下几个关键属性:

  • m_ids:生成 Read View 时,系统中活跃的(未提交的)读写事务的事务ID列表。
  • min_trx_idm_ids 中的最小值。
  • max_trx_id:生成 Read View 时,系统应该分配给下一个事务的ID。(注意:不是 m_ids 的最大值,而是已创建的最大事务ID+1)。
  • creator_trx_id:创建该 Read View 的事务ID。

三、可见性算法:如何判断一个版本是否可见?

当一个事务执行一条 SELECT 语句(快照读)时,它需要遍历数据行的版本链,并利用自己的 Read View,通过一套算法来决定哪个版本对它来说是可见的。

对于版本链中的某个版本,假设其对应的事务ID为 trx_id,判断规则如下:

  • 如果 trx_id == creator_trx_id
    • 说明当前事务自己修改了这行数据,这个版本是可见的
  • 如果 trx_id < min_trx_id
    • 说明这个版本是在当前 Read View 创建之前就已经提交的,这个版本是可见的
  • 如果 trx_id >= max_trx_id
    • 说明这个版本是在当前 Read View 创建之后才开启的事务修改的,这个版本不可见
  • 如果 min_trx_id <= trx_id < max_trx_id
    • 需要检查 trx_id 是否在 m_ids(活跃事务列表)中:
      • 如果在,说明创建 Read View 时,修改这个版本的事务还未提交,该版本不可见
      • 如果不在,说明创建 Read View 时,修改这个版本的事务已经提交了,该版本可见

四、在不同隔离级别下的表现

MVCC 主要在 READ COMMITTEDREPEATABLE READ 这两个隔离级别下工作。

1. REPEATABLE READ(可重复读 - MySQL 默认级别)

  • 核心特性: 在同一个事务中,第一次执行快照读时会创建一个 Read View,之后在这个事务中的所有快照读都复用这个相同的 Read View
  • 效果: 因为 Read View 是静态的,所以无论之后其他事务如何提交修改,这个事务看到的数据快照始终和它第一次看到的一样。这就完美实现了“可重复读”。
2. READ COMMITTED(读已提交)

  • 核心特性: 在同一个事务中,每次执行快照读都会生成一个新的、独立的 Read View
  • 效果: 因为每次读都会重新获取一次当前系统的活跃事务列表,所以每次都能看到在本次查询开始之前已经提交的所有事务的修改。这就实现了“读已提交”,即能读到其他事务最新提交的内容。

五、总结与流程图

MVCC 工作流程总结:

  1. 每个数据行都有隐藏的 DB_TRX_IDDB_ROLL_PTR
  2. 修改操作会在 Undo Log 中创建历史版本,形成版本链。
  3. 事务在快照读时生成 Read View(RC每次生成,RR第一次生成)。
  4. 通过可见性算法,遍历版本链,找到对当前事务可见的那个数据版本。

判断流程图:

六、补充说明

  • 快照读 vs 当前读
    • 快照读:普通的 SELECT 语句,基于 MVCC 和 Read View 读取历史版本,不加锁。
    • 当前读:特殊的 SELECT 语句(如 SELECT ... FOR UPDATESELECT ... LOCK IN SHARE MODE)以及 INSERTUPDATEDELETE。当前读读取的是记录的最新版本,并且会通过加锁(Next-Key Lock)来保证数据一致性。
  • Purge 操作
    • 随着时间推移,Undo Log 中旧版本数据会越来越多。系统会有一个后台的 Purge 线程来清理那些不再被任何事务的 Read View 需要的旧版本数据,从而释放存储空间。

如果小假的内容对你有帮助,请点赞评论收藏。创作不易,大家的支持就是我坚持下去的动力!

Read more

【Linux系统编程】(四十二)吃透线程互斥!从原理到实战,手把手教你玩转 Linux 下的互斥锁

【Linux系统编程】(四十二)吃透线程互斥!从原理到实战,手把手教你玩转 Linux 下的互斥锁

目录 前言 一、线程互斥的核心概念:搞懂这些,才算入门 1.1 共享资源与临界资源 1.2 临界区 1.3 互斥的定义 1.4 原子性:互斥的底层要求 二、多线程共享资源的坑:亲眼看看问题出在哪 2.1 问题代码:未加互斥的售票系统 2.2 编译运行与异常结果 2.3 问题根源:三步分析 (1)线程调度的随机性 (2)耗时操作放大了竞争问题 (3)ticket--本身不是原子操作 2.4 解决问题的核心要求 三、Linux 下的互斥量:mutex 的使用全解析 3.1 互斥量的类型与核心接口

By Ne0inhk

Flutter 三方库 at_server_status 的鸿蒙化适配指南 - 在鸿蒙系统上构建极致、透明、实时的 @protocol 去中心化身份服务器状态感知与鉴权监控引擎

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 at_server_status 的鸿蒙化适配指南 - 在鸿蒙系统上构建极致、透明、实时的 @protocol 去中心化身份服务器状态感知与鉴权监控引擎 在鸿蒙(OpenHarmony)系统的隐私保护应用、去中心化身份管理工具(基于 @protocol 协议)或需要实时监控全球分布式节点健康状况的场景中,如何判定一个 @sign(电子签名标识)背后的 Root 服务器或 Secondary 服务器是否在线、配置是否由于由于由于由于已就绪?at_server_status 为开发者提供了一套工业级的、基于协议栈的状态审计与自检方案。本文将深入实战其在鸿蒙 Web3 身份安全底座中的应用。 前言 什么是 atServer Status?它是 @protocol(一种旨在让用户完全掌控数据的去中心化协议)官方生态的核心组件。

By Ne0inhk
鸿蒙金融理财全栈项目——生态合作、用户运营、数据变现

鸿蒙金融理财全栈项目——生态合作、用户运营、数据变现

《鸿蒙APP开发从入门到精通》第19篇:鸿蒙金融理财全栈项目——生态合作、用户运营、数据变现 📊🌍💰 内容承接与核心价值 这是《鸿蒙APP开发从入门到精通》的第19篇——生态合作、用户运营、数据变现篇,100%承接第18篇的风险控制、合规审计、产品创新架构,并基于金融场景的生态合作、用户运营、数据变现要求,设计并实现鸿蒙金融理财全栈项目的生态合作、用户运营、数据变现功能。 学习目标: * 掌握鸿蒙金融理财项目的生态合作设计与实现; * 实现金融机构合作、支付渠道合作、数据分析合作; * 理解用户运营在金融场景的核心设计与实现; * 实现用户增长、用户留存、用户转化; * 掌握数据变现在金融场景的设计与实现; * 实现数据服务、数据产品、数据变现; * 优化金融理财项目的用户体验(生态合作、用户运营、数据变现)。 学习重点: * 鸿蒙金融理财项目的生态合作设计原则; * 用户运营在金融场景的应用; * 数据变现在金融场景的设计要点。 一、 生态合作基础 🎯 1.1 生态合作定义 生态合作是指金融理财项目与其他金融机构、

By Ne0inhk

Flutter 三方库 encrypter_plus 的鸿蒙化适配指南 - 打造工业级多重加密隔离、安全存储实战、鸿蒙级数据隐私专家

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 encrypter_plus 的鸿蒙化适配指南 - 打造工业级多重加密隔离、安全存储实战、鸿蒙级数据隐私专家 在鸿蒙跨平台应用处理用户核心资产、敏感通讯或离线隐私数据库时,单一的加密手段往往难以应对复杂的逆向工程攻击。我们需要一套功能全面、算法严谨且易于在鸿蒙端进行多层加固的方案。今天我们要深度解析的 encrypter_plus——一个集成了 AES、RSA、Salsa20 等多种主流算法的增强型加密工具集,正是帮你构建“数据保险柜”的核心组件。 前言 encrypter_plus 是对经典 encrypt 库的功能增强与性能优化版。它提供了更直观的操作符抽象和更健壮的填充(Padding)机制。在鸿蒙端项目中,利用它你可以轻松实现前端文件加密、服务端通讯非对称握手以及本地敏感配置的字段级混淆,确保即使用户设备的物理文件被导出,数据依然处于不可读的“致密状态”。 一、原理解析 / 概念介绍 1.1

By Ne0inhk