Jw
首页
技术
英语
首页
技术
英语
  • MySQL

    • MySQL 基础
    • 环境变量配置(PATH)
    • SQL 语法与分类
    • MySQL 数据类型
    • SQL - DDL 数据定义语言
    • SQL - DML 数据操作语言
    • SQL - DQL 数据查询语言
    • SQL - DCL 数据控制语言
    • MySQL 函数
    • SQL 约束
    • SQL 外键约束
    • 多表关系
    • 多表查询(连接查询)
    • 隐式内连接 vs 显式内连接
    • 子查询
    • 多表查询案例
    • 事务
    • 并发事务问题与隔离级别
    • MySQL 体系结构与存储引擎
    • 索引概述
    • 索引结构
    • 索引分类
    • 索引语法
    • SQL 性能分析
    • 索引使用
    • 索引设计原则
    • SQL 优化 - 插入数据与主键优化
    • SQL 优化 - order by 与 group by
    • SQL 优化 - limit / count / update
    • 视图
    • 存储过程
    • 存储过程 - 变量
    • 存储过程 - 流程控制、游标与条件处理
    • 存储函数
    • 触发器
    • 锁概述与全局锁
    • 表级锁
    • 行级锁
    • InnoDB 逻辑存储结构
    • InnoDB 架构
    • 事务原理(redo / undo log)
    • MVCC
    • MySQL 系统数据库
    • MySQL 常用工具
    • 数据备份与导入导出
    • 错误日志与二进制日志
    • 查询日志与慢查询日志
    • 主从复制 - 概述与原理
    • 主从复制 - 搭建
    • Mycat 配置
    • Mycat 垂直拆分与水平拆分
    • Mycat 分片规则
    • Mycat 管理与监控
    • Mycat 读写分离 - 一主一从
    • Mycat 读写分离 - 双主双从

MVCC

MVCC(Multi-Version Concurrency Control,多版本并发控制)通过维护数据的多个版本,让读写不冲突:读走快照、写加锁,从而实现非阻塞读。

一、当前读与快照读

类型读取内容是否加锁典型语句
当前读记录的最新版本,并保证其他并发事务不能修改当前记录加锁SELECT ... LOCK IN SHARE MODE(S 锁)、SELECT ... FOR UPDATE、UPDATE、INSERT、DELETE(X 锁)
快照读记录的可见版本,可能是历史数据不加锁、非阻塞简单的 SELECT(不加锁)

快照读是 MySQL 实现 MVCC 提供的非阻塞读功能;当前读则依赖锁。

各隔离级别下的快照读

隔离级别快照读行为
Read Committed每次 SELECT 都生成一个快照读(新的 ReadView)
Repeatable Read开启事务后第一个 SELECT 才是快照读的地方,后续复用该 ReadView
Serializable快照读退化为当前读

二、实现三要素

MVCC 的具体实现依赖:记录中的隐藏字段、undo log 日志、ReadView。

2.1 隐藏字段

隐藏字段含义
DB_TRX_ID最近修改事务 ID:记录插入这条记录或最后一次修改该记录的事务 ID
DB_ROLL_PTR回滚指针,指向这条记录的上一个版本,配合 undo log 使用
DB_ROW_ID隐藏主键:表结构没有指定主键时,会生成该隐藏字段

2.2 undo log 版本链

不同事务(或同一事务)对同一条记录进行修改,会让该记录的 undo log 形成一条版本链表:链表头部是最新的旧记录,尾部是最早的旧记录。

示例:id = 30 的记录被事务 2、3、4 依次修改:

版本idagenameDB_TRX_IDDB_ROLL_PTR
当前记录3010A340x00003
undo log303A330x00002
undo log303A3020x00001
undo log3030A301null

顺着 DB_ROLL_PTR 就能从当前版本一路回溯到最早版本,快照读正是靠它找到「自己该看到的那一版」。

2.3 ReadView(读视图)

ReadView 是快照读 SQL 执行时 MVCC 提取数据的依据,记录并维护系统当前活跃事务(未提交)的 ID。

字段含义
m_ids当前活跃的事务 ID 集合
min_trx_id最小活跃事务 ID
max_trx_id预分配事务 ID,即当前最大事务 ID + 1(事务 ID 自增)
creator_trx_idReadView 创建者的事务 ID

三、版本链数据访问规则

设 trx_id 为当前记录某个版本的事务 ID,按顺序判断:

#判断能否访问说明
①trx_id == creator_trx_id可以数据是当前这个事务更改的
②trx_id < min_trx_id可以数据已经提交了
③trx_id > max_trx_id不可以该事务是在 ReadView 生成后才开启的
④min_trx_id <= trx_id <= max_trx_id若 trx_id 不在 m_ids 中则可以数据已经提交

若当前版本不可访问,就顺着 DB_ROLL_PTR 找上一个版本,重复上述判断,直到找到可见版本。

四、RC 与 RR 的区别

隔离级别ReadView 生成时机效果
READ COMMITTED事务中每一次执行快照读时都生成每次都能读到别人最新提交的数据 → 不可重复读
REPEATABLE READ仅在事务中第一次执行快照读时生成,后续复用整个事务看到的快照不变 → 可重复读

RC 示例:事务 2 / 3 / 4 / 5 并发,事务 5 两次查询之间事务 3、4 先后提交。

时刻事务 2事务 3事务 4事务 5
①开始事务开始事务开始事务开始事务
②修改 id=30,age 改为 3查询 id=30
③提交事务
④修改 id=30,name 改为 A3查询 id=30
⑤提交事务
⑥修改 id=30,age 改为 10;查询 id=30
⑦查询 id=30
⑧提交事务
  • 事务 5 第 ④ 步生成 ReadView:m_ids = {3,4,5}、min_trx_id = 3、max_trx_id = 6、creator_trx_id = 5。
  • 事务 5 第 ⑦ 步重新生成 ReadView:m_ids = {4,5}、min_trx_id = 4、max_trx_id = 6。
  • 所以 RC 下两次查询结果不同(读到事务 3 提交后的数据),表现为不可重复读。

RR 示例:同样的时序,事务 5 第二次查询复用第一次的 ReadView,所以两次结果一致,表现为可重复读。

五、整体关系

MVCC = 隐藏字段 + undo log 版本链 + ReadView
隔离性 = MVCC + 锁
一致性 = undo log + redo log

速查

-- 查看当前隔离级别(RR 为默认)
SELECT @@transaction_isolation;

-- 修改隔离级别为 RC:每次快照读都生成新 ReadView
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- 快照读:不加锁,读可见版本
SELECT * FROM stu WHERE id = 30;

-- 当前读:加共享锁 / 排他锁,读最新版本
SELECT * FROM stu WHERE id = 30 LOCK IN SHARE MODE;
SELECT * FROM stu WHERE id = 30 FOR UPDATE;
要做什么结论
读不加锁、不阻塞写快照读(普通 SELECT)
读到最新数据并锁住当前读(FOR UPDATE / LOCK IN SHARE MODE)
RR 下两次查询结果一致复用第一次快照读生成的 ReadView
RC 下能读到新提交的数据每次快照读都重新生成 ReadView

相关

  • 事务原理:undo log 如何支撑 MVCC
  • InnoDB 逻辑存储结构:Trx_id、Roll_pointer 隐藏字段
  • 并发事务问题与隔离级别:脏读 / 不可重复读 / 幻读
  • 行级锁:当前读所加的锁
最近更新: 2026/10/25 11:01
Contributors: liujw
Prev
事务原理(redo / undo log)
Next
MySQL 系统数据库