MVCC 全称是 Multi-Version Concurrency Control也就是多版本并发控制。它的核心思想是为同一行数据维护多个版本让读写在很多情况下不用互相阻塞。没有 MVCC 时读写冲突通常要大量依赖锁。MVCC 让普通select可以读一个可见的数据版本而不是必须等待正在修改这行数据的事务结束。MVCC 依赖哪三样东西InnoDB 的 MVCC 主要依赖记录中的隐藏字段。undo log 版本链。ReadView 读视图。这三者配合解决一个问题当前事务应该看到这行数据的哪个版本记录中的隐藏字段InnoDB 每行记录除了业务字段还会维护一些隐藏字段。隐藏字段含义DB_TRX_ID最近一次修改这行记录的事务 IDDB_ROLL_PTR回滚指针指向 undo log 中的上一个版本DB_ROW_ID隐藏行 ID没有主键时可能使用其中DB_TRX_ID和DB_ROLL_PTR是理解 MVCC 的关键。undo log 版本链当一行数据被多个事务修改时undo log 会形成一条版本链。链表头部通常靠近较新的旧版本沿着roll_pointer可以找到更早的版本。例如一条记录从A1被改成A2又被改成A3。当前数据页里是最新值而 undo log 中保存旧值。不同事务根据可见性规则可能读到A3、A2或A1。当前读和快照读不是所有读都走 MVCC 快照。类型SQL 示例特点当前读select ... for update、update、delete、insert读取最新版本并加锁快照读普通select读取可见版本不加锁非阻塞MVCC 主要服务于快照读。普通select不加锁也能在并发写入时稳定读取就是因为它读的是某个可见版本。ReadView 是什么ReadView 是快照读执行时生成的读视图用来判断版本链上的某个版本是否对当前事务可见。ReadView 中有四个核心字段字段含义creator_trx_id创建这个 ReadView 的事务 IDm_ids创建 ReadView 时活跃且未提交的事务 ID 集合min_trx_id活跃事务中的最小 IDmax_trx_id下一个将要分配的事务 ID版本可见性规则当沿着版本链找到某个版本时会拿这个版本的trx_id和 ReadView 比较。这块最适合按流程走。每遇到一个版本就问几个问题是否是否是否是否快照读开始生成或复用 ReadView读取当前记录版本trx_id 是否等于 creator_trx_id版本可见trx_id 是否小于 min_trx_idtrx_id 是否大于等于 max_trx_id版本不可见trx_id 是否在 m_ids 中沿 undo log 版本链找上一个版本规则可以简化为trx_id creator_trx_id自己改的能看见。trx_id min_trx_id这个版本在 ReadView 生成前已经提交能看见。trx_id max_trx_id这个版本在 ReadView 生成后才出现不能看见。min_trx_id trx_id max_trx_id如果trx_id不在m_ids中说明已提交能看见如果在m_ids中说明当时还活跃不能看见。这些规则的目的很朴素只让当前事务看到它应该看到的已提交版本。RC 和 RR 的区别READ COMMITTED和REPEATABLE READ的核心差异之一是生成 ReadView 的时机不同。隔离级别ReadView 生成时机结果READ COMMITTED每次执行快照读都生成新的 ReadView同一事务内多次查询可能看到新提交数据REPEATABLE READ第一次快照读生成 ReadView后续复用同一事务内多次查询结果更稳定两种隔离级别的差别可以这样看RCRR同一个事务内多次普通 select隔离级别每次 select 生成新的 ReadView可能看到其他事务新提交的数据第一次 select 生成 ReadView后续 select 复用同一个 ReadView同一事务内读取结果更稳定这也解释了为什么 RC 下可能出现不可重复读而 RR 能让普通快照读保持可重复。一个简单例子假设事务 5 创建了 ReadViewm_ids {3, 4, 5} min_trx_id 3 max_trx_id 6 creator_trx_id 5现在沿着版本链看到几个版本版本 trx_id是否可见原因5可见当前事务自己修改2可见小于min_trx_id早已提交6不可见不小于max_trx_id属于更晚事务4不可见在m_ids中当时未提交如果某个版本不可见就继续沿着 undo log 版本链向前找直到找到可见版本或没有更旧版本。面试回答模板可以这样回答MVCC 是多版本并发控制用来降低读写冲突。InnoDB 每行记录有隐藏字段比如事务 ID 和回滚指针更新数据时会生成 undo log多个旧版本通过回滚指针形成版本链。普通 select 是快照读会基于 ReadView 判断版本是否可见。ReadView 中包含当前活跃事务集合、最小活跃事务 ID、下一个事务 ID 和创建者事务 ID。RC 隔离级别下每次快照读都会生成新的 ReadViewRR 隔离级别下第一次快照读生成后会复用所以 RR 下多次读取更稳定。小结MVCC 的难点不是名词多而是要把它们串成一条线隐藏字段记录版本信息undo log 串起旧版本ReadView 决定哪个版本可见。理解这条线MVCC 就从“背概念”变成了“顺着链表找可见数据”。
MySQL-MVCC核心原理-版本链ReadView与可见性判断
发布时间:2026/5/17 3:36:50
MVCC 全称是 Multi-Version Concurrency Control也就是多版本并发控制。它的核心思想是为同一行数据维护多个版本让读写在很多情况下不用互相阻塞。没有 MVCC 时读写冲突通常要大量依赖锁。MVCC 让普通select可以读一个可见的数据版本而不是必须等待正在修改这行数据的事务结束。MVCC 依赖哪三样东西InnoDB 的 MVCC 主要依赖记录中的隐藏字段。undo log 版本链。ReadView 读视图。这三者配合解决一个问题当前事务应该看到这行数据的哪个版本记录中的隐藏字段InnoDB 每行记录除了业务字段还会维护一些隐藏字段。隐藏字段含义DB_TRX_ID最近一次修改这行记录的事务 IDDB_ROLL_PTR回滚指针指向 undo log 中的上一个版本DB_ROW_ID隐藏行 ID没有主键时可能使用其中DB_TRX_ID和DB_ROLL_PTR是理解 MVCC 的关键。undo log 版本链当一行数据被多个事务修改时undo log 会形成一条版本链。链表头部通常靠近较新的旧版本沿着roll_pointer可以找到更早的版本。例如一条记录从A1被改成A2又被改成A3。当前数据页里是最新值而 undo log 中保存旧值。不同事务根据可见性规则可能读到A3、A2或A1。当前读和快照读不是所有读都走 MVCC 快照。类型SQL 示例特点当前读select ... for update、update、delete、insert读取最新版本并加锁快照读普通select读取可见版本不加锁非阻塞MVCC 主要服务于快照读。普通select不加锁也能在并发写入时稳定读取就是因为它读的是某个可见版本。ReadView 是什么ReadView 是快照读执行时生成的读视图用来判断版本链上的某个版本是否对当前事务可见。ReadView 中有四个核心字段字段含义creator_trx_id创建这个 ReadView 的事务 IDm_ids创建 ReadView 时活跃且未提交的事务 ID 集合min_trx_id活跃事务中的最小 IDmax_trx_id下一个将要分配的事务 ID版本可见性规则当沿着版本链找到某个版本时会拿这个版本的trx_id和 ReadView 比较。这块最适合按流程走。每遇到一个版本就问几个问题是否是否是否是否快照读开始生成或复用 ReadView读取当前记录版本trx_id 是否等于 creator_trx_id版本可见trx_id 是否小于 min_trx_idtrx_id 是否大于等于 max_trx_id版本不可见trx_id 是否在 m_ids 中沿 undo log 版本链找上一个版本规则可以简化为trx_id creator_trx_id自己改的能看见。trx_id min_trx_id这个版本在 ReadView 生成前已经提交能看见。trx_id max_trx_id这个版本在 ReadView 生成后才出现不能看见。min_trx_id trx_id max_trx_id如果trx_id不在m_ids中说明已提交能看见如果在m_ids中说明当时还活跃不能看见。这些规则的目的很朴素只让当前事务看到它应该看到的已提交版本。RC 和 RR 的区别READ COMMITTED和REPEATABLE READ的核心差异之一是生成 ReadView 的时机不同。隔离级别ReadView 生成时机结果READ COMMITTED每次执行快照读都生成新的 ReadView同一事务内多次查询可能看到新提交数据REPEATABLE READ第一次快照读生成 ReadView后续复用同一事务内多次查询结果更稳定两种隔离级别的差别可以这样看RCRR同一个事务内多次普通 select隔离级别每次 select 生成新的 ReadView可能看到其他事务新提交的数据第一次 select 生成 ReadView后续 select 复用同一个 ReadView同一事务内读取结果更稳定这也解释了为什么 RC 下可能出现不可重复读而 RR 能让普通快照读保持可重复。一个简单例子假设事务 5 创建了 ReadViewm_ids {3, 4, 5} min_trx_id 3 max_trx_id 6 creator_trx_id 5现在沿着版本链看到几个版本版本 trx_id是否可见原因5可见当前事务自己修改2可见小于min_trx_id早已提交6不可见不小于max_trx_id属于更晚事务4不可见在m_ids中当时未提交如果某个版本不可见就继续沿着 undo log 版本链向前找直到找到可见版本或没有更旧版本。面试回答模板可以这样回答MVCC 是多版本并发控制用来降低读写冲突。InnoDB 每行记录有隐藏字段比如事务 ID 和回滚指针更新数据时会生成 undo log多个旧版本通过回滚指针形成版本链。普通 select 是快照读会基于 ReadView 判断版本是否可见。ReadView 中包含当前活跃事务集合、最小活跃事务 ID、下一个事务 ID 和创建者事务 ID。RC 隔离级别下每次快照读都会生成新的 ReadViewRR 隔离级别下第一次快照读生成后会复用所以 RR 下多次读取更稳定。小结MVCC 的难点不是名词多而是要把它们串成一条线隐藏字段记录版本信息undo log 串起旧版本ReadView 决定哪个版本可见。理解这条线MVCC 就从“背概念”变成了“顺着链表找可见数据”。