【Mysql】MySQL的MVCC及实现原理,核心目标与全流程图解

一、MVCC核心

MySQL MVCC(多版本并发控制)是 ​InnoDB 引擎实现高并发的核心技术,其本质是 ​通过数据多版本实现读写操作的并行执行。

解决两大问题:

  1. 读写冲突:读操作不阻塞写操作
  2. 事务隔离:实现RC(读已提交)、RR(可重复读)隔离级别

📌 关键设计:通过数据多版本+版本可见性判断实现

MVCC 全流程图解

不可见
事务开始
分配trx_id
数据修改
写Undo Log
更新行DB_TRX_ID/DB_ROLL_PTR
读操作
创建ReadView
检查当前行可见性
沿Undo链回溯
找到可见版本
Purge线程
清理过期Undo Log

二、MVCC核心实现组件

1. 隐藏字段(InnoDB表必有)

字段名长度作用
DB_TRX_ID6字节最新修改该行的事务ID
DB_ROLL_PTR7字节指向Undo Log的回滚指针
DB_ROW_ID6字节行隐藏ID(无主键时自增)
-- 查看行隐藏字段(虚拟列)
SELECT 
  `DB_TRX_ID`,   -- 最后更新事务ID
  `DB_ROLL_PTR`, -- 指向历史版本链
  `DB_ROW_ID`    -- 行唯一标识
FROM information_schema.INNODB_SYS_TABLES 
WHERE name = 'test/t1';

2. Undo Log(版本链)

当前行数据
Undo Log v1
Undo Log v2
Undo Log v3
  • 更新流程:每次修改前拷贝旧数据到Undo Log
  • 版本链:通过DB_ROLL_PTR串联历史版本

3. ReadView(可见性判断核心)

// 源码位置:storage/innobase/include/read0types.h
class ReadView {
  trx_id_t m_low_limit_id;    // 高水位ID(>此值的事务不可见)
  trx_id_t m_up_limit_id;     // 低水位ID(<此值的事务可见)
  ids_t    m_ids;             // 活跃事务ID列表
  trx_id_t m_creator_trx_id;  // 创建该ReadView的事务ID
};

三、可见性判断算法(RR隔离示例)

等于当前事务ID
小于m_up_limit_id
大于m_low_limit_id
在m_ids中
其他情况
读取数据行
检查DB_TRX_ID
可见
可见
不可见
不可见
可见

代码级判断逻辑:

// storage/innobase/include/read0types.h
bool changes_visible(trx_id_t id, const table_name_t& name) const {
  // 1. 自己修改的数据总是可见
  if (id == m_creator_trx_id) return true; 
  
  // 2. 事务ID < 活跃事务最小ID(已提交)
  if (id < m_up_limit_id) return true;
  
  // 3. 事务ID >= 高水位(属于未来事务)
  if (id >= m_low_limit_id) return false;
  
  // 4. 事务活跃状态检查
  return (m_ids.find(id) == m_ids.end()); // 不在活跃列表中则可见
}

四、不同隔离级别的实现差异

隔离级别ReadView创建时机版本链访问逻辑
RC(读已提交)每次SELECT创建新ReadView总是读取最新已提交版本
RR(可重复读)首次SELECT创建ReadView复用同一视图,保持版本一致性
Serializable加锁读(不使用MVCC)无版本链

⚠️ RR防幻读机制:基于Next-Key Locking + MVCC版本快照


五、实战案例演示(RC vs RR)

-- 初始化数据
CREATE TABLE t(id INT PRIMARY KEY, v INT);
INSERT INTO t VALUES(1, 10);

-- 事务1(更新数据)
START TRANSACTION;
UPDATE t SET v = 20 WHERE id = 1;

-- 事务2(两次查询)
START TRANSACTION;
SELECT v FROM t WHERE id = 1; -- RC看到20|RR看到10(旧值)
COMMIT;

-- 事务1提交
COMMIT;

-- 事务2再次查询
SELECT v FROM t WHERE id = 1; -- RC/RR均看到20

结果对比:

步骤RC隔离级别结果RR隔离级别结果
事务2首次查询20(脏读)10(快照读)
事务2二次查询20(读已提交)10(可重复读)

六、关键参数优化

# my.cnf配置项
innodb_undo_log_truncate = ON      # 启用undo log清理
innodb_max_undo_log_size = 1G      # 控制版本链长度
innodb_purge_threads = 4           # 后台清理线程数

七、MVCC的限制

  1. 大事务风险:长事务导致Undo Log暴涨(可能触发OOM)
  2. 历史版本清理:purge线程滞后可能引发性能问题
  3. 二级索引更新:仅主键存储版本链,二级索引通过主键回表判断可见性

📊 监控建议:定期检查SHOW ENGINE INNODB STATUS中的History list length


通过【隐藏字段+Undo版本链+ReadView】三元组配合,MySQL实现在不锁定读的情况下保证事务隔离。核心在于多版本数据存储+事务快照判断,此设计也是InnoDB高并发能力的基石。

<think>嗯,用户之前问了关于MySQL的Double Write、Redo Log和Undo Log的关系,现在接着问MVCC原理,最好有图。看来用户对MySQL的事务和并发控制机制比较感兴趣,可能是在学习数据库的内部原理,或者是在准备面试之类的。需要先回顾一下之前的对话,确保这次回答能衔接上。 首先,用户的问题是关于MVCC原理,而且希望有图示。不过由于是文字回复,不能直接画图,但可以用文字描述图的结构,或者用符号模拟图表。比如用表格表示不同版本的数据,或者时间线来展示事务的可见性。 MVCC,即多版本并发控制,是MySQL中InnoDB存储引擎实现事务隔离级别的重要机制,尤其是读已提交(RC)和可重复读(RR)级别。它的核心是通过保存数据的历史版本来避免读写冲突,提高并发性能。需要解释清楚版本链、Read View这些关键概念。 接下来要确定用户可能对哪些部分不太清楚。比如,版本链是如何形成的,Undo Log在这里的作用,Read View如何判断可见性。同时,用户可能想知道MVCC如何解决幻读问题,尤其是在RR级别下。但根据之前的问题,用户已经了解Undo Log,所以可以结合之前的Undo Log知识来讲解。 可能需要注意的点: 1. 每个数据行的隐藏字段:DB_TRX_ID(事务ID)、DB_ROLL_PTR(回滚指针)、DB_ROW_ID(行ID)。 2. Undo Log存储旧版本数据,形成版本链。 3. Read View的结构,包括活跃事务列表、最小事务ID、最大事务ID等。 4. 可见性判断规则,如何根据事务ID和Read View来决定读取哪个版本的数据。 5. 不同隔离级别下Read View生成的时机,比如RC每次读取生成,RR在第一次读取时生成。 需要举例说明,比如两个事务同时操作同一行数据,如何通过版本链和Read View实现读写不阻塞。同时,可能需要解释快照读和当前读的区别,比如普通select是快照读,而update、delete、select for update是当前读。 用户要求最好有图,但无法实际画图,所以需要用文字模拟图表。比如用表格展示不同事务对同一行的修改,形成版本链,再用另一个表格描述Read View的内容,然后说明如何根据这些信息判断可见性。 另外,需要注意用户的背景。如果用户是开发者或DBA,可能需要更深入的技术细节;如果是学生或初学者,可能需要更通俗的解释。根据之前的问题,用户对Redo Log和Undo Log有一定了解,所以可以适当深入,但保持清晰。 最后,总结MVCC的优点和适用场景,以及它如何其他机制(如锁)配合使用,比如在RR级别下,MVCC解决了大部分幻读问题,但某些情况下仍需加锁。 现在需要组织这些内容,分步骤解释MVCC原理,结合示例和模拟图表,确保逻辑清晰,用户容易理解。</think>### **MVCC(多版本并发控制)原理详解** MVCC(Multi-Version Concurrency Control)是 MySQL InnoDB 存储引擎实现事务隔离级别的核心机制,主要用于解决**读写冲突**,提升并发性能。它通过保存数据的历史版本,使得不同事务可以“看到”不同版本的数据,从而避免加锁阻塞。以下是其核心原理实现细节: --- ### **一、核心思想** MVCC核心是:**为每条数据维护多个版本**,每个事务根据自身时间戳或状态,决定读取哪个版本的数据。 - **读写不冲突**:读操作读取历史快照(不阻塞写),写操作生成新版本(不阻塞读)。 - **隔离性实现**:不同隔离级别(如 RC、RR)通过控制版本可见性规则实现。 --- ### **二、核心组件** #### **1. 数据行的隐藏字段** InnoDB 表中每行数据包含以下隐藏字段: | 字段名 | 描述 | |----------------|----------------------------------------| | `DB_TRX_ID` | 最近修改该行的事务ID(事务开始时分配) | | `DB_ROLL_PTR` | 指向旧版本数据的指针(通过 Undo Log 构建版本链) | | `DB_ROW_ID` | 行唯一标识(当无主键时自动生成) | #### **2. Undo Log(回滚日志)** - 存储数据修改前的历史版本,形成**版本链**。 - 每次更新/删除数据时,旧版本会被存入 Undo Log,并通过 `DB_ROLL_PTR` 指针串联成链表。 - **示例版本链**(假设事务按顺序修改数据): ``` 当前行 → 版本1(事务100) ← 版本2(事务200) ← 版本3(事务300) ↑ ↑ DB_ROLL_PTR DB_ROLL_PTR ``` #### **3. Read View(一致性视图)** 事务在查询时生成一个 Read View,用于判断数据版本的可见性。其关键属性包括: | 属性 | 描述 | |-----------------------|-----------------------------------------| | `m_ids` | 当前活跃(未提交)的事务ID集合 | | `min_trx_id` | 活跃事务中的最小事务ID | | `max_trx_id` | 下一个即将分配的事务ID(即当前最大事务ID+1) | | `creator_trx_id` | 创建该 Read View 的事务ID | --- ### **三、可见性判断规则** 事务读取数据时,按以下规则选择可见的版本(遍历版本链): 1. **版本的事务ID < `min_trx_id`** → 该版本已提交,对当前事务可见。 2. **版本的事务ID ≥ `max_trx_id`** → 该版本由未来事务修改,不可见。 3. **`min_trx_id` ≤ 版本的事务ID < `max_trx_id`** - 若事务ID在 `m_ids` 中 → 事务未提交,不可见。 - 若事务ID不在 `m_ids` 中 → 事务已提交,可见。 **特殊情况**: - 若版本的事务ID = `creator_trx_id` → 当前事务自身修改的数据,可见。 --- ### **四、示例图解** 假设有以下事务和版本链(文字模拟图表): #### **1. 数据行版本链** | 版本 | 事务ID | 数据内容 | DB_ROLL_PTR | |------|--------|----------|-------------------| | V3 | 300 | "Data C" | → V2 (事务200) | | V2 | 200 | "Data B" | → V1 (事务100) | | V1 | 100 | "Data A" | NULL | #### **2. 事务场景** - **事务400**:启动后查询数据。 - 当前活跃事务:`m_ids = [200, 300]`,`min_trx_id=200`,`max_trx_id=401`。 #### **3. 可见性判断** - **事务400** 读取数据时,遍历版本链: - **V3(事务300)**:事务ID=300 ∈ `m_ids` → 未提交,不可见。 - **V2(事务200)**:事务ID=200 ∈ `m_ids` → 未提交,不可见。 - **V1(事务100)**:事务ID=100 < `min_trx_id` → 已提交,可见。 → 最终读到 "Data A"。 --- ### **五、隔离级别 MVCC** 1. **Read Committed(RC)** - 每次查询生成新的 Read View。 - 可能读到其他事务已提交的新数据。 2. **Repeatable Read(RR)** - 在事务首次查询时生成 Read View,后续复用。 - 通过**一致性快照**避免不可重复读和幻读(多数场景)。 --- ### **六、MVCC 如何解决幻读?** - **快照读**(普通 `SELECT`):通过 Read View 读取历史版本,其他事务的插入/删除不影响当前事务的可见性。 - **当前读**(`SELECT FOR UPDATE`、`UPDATE`):需加锁,可能阻塞其他事务的写入,严格保证数据最新状态。 --- ### **七、总结** - **MVCC 核心**:通过版本链 + Read View 实现读写并行。 - **优点**:减少锁竞争,提升并发性能。 - **依赖机制**:Undo Log 存储历史版本,Redo Log 保证持久性。 通过 MVCCMySQL 在保证事务隔离性的同时,大幅提高了高并发场景下的性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜雨hiyeyu.com

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值