MVCC是 InnoDB 实现非阻塞读的核心技术,其核心思想是:允许多个事务同时读取同一行数据而不相互阻塞,每个事务只能看到自身可见的数据版本。即使其他事务在期间修改了数据,当前事务仍能看到事务启动时的数据状态,从而在无锁情况下实现高效并发。
一、Read View:控制可见性的快照视图
Read View 是 MVCC 实现版本可见性判断的关键机制,它本身不存储数据,仅记录判断快照可见性的元信息,配合undo log中保存的历史版本数据,共同决定事务能看到哪个版本的记录。
1. Read View 的四个核心字段
-
m_ids:生成 Read View 时,当前所有活跃(未提交)事务的 ID 集合。
-
min_trx_id:m_ids 集合中的最小事务 ID(若 m_ids 为空,则等于 max_trx_id)。
-
max_trx_id:生成 Read View 时,系统下一个待分配的事务 ID(即当前全局最大事务 ID+1,非 m_ids 中的最大值)。
-
creator_trx_id:生成当前 Read View 的事务自身 ID(即当前查询事务的 ID)。
2. 数据版本的基础:隐藏列
InnoDB 的聚簇索引记录中包含两个隐藏列,用于支持版本链管理:
-
trx_id:记录最后修改该数据的事务 ID(事务修改数据时写入)。
-
roll_pointer:指向 undo log 中该记录的上一个版本,形成版本链(每次修改都会生成新版本,旧版本存入 undo log),当某版本对当前 Read View 不可见时,存储引擎会沿 roll_pointer 在 undo log 中回溯,直到找到对当前快照可见的版本。
二、隔离级别与 Read View 的生成时机
读已提交(RC)和可重复读(RR)隔离级别的核心差异,在于 Read View 的生成时机不同,这直接影响事务看到的数据版本:
-
**读已提交(RC):**每次执行快照读时都会重新生成 Read View。因此,同一事务中多次查询可能看到不同的提交版本(可能出现 "不可重复读")。
-
**可重复读(RR):**事务中第一次执行快照读时生成 Read View,后续查询复用该 Read View。因此,同一事务中多次查询看到的是同一快照,避免 "不可重复读"。
三、数据版本的可见性判断逻辑
基于 Read View 的字段,事务访问记录时,通过比较记录的trx_id
与 Read View 字段,判断该版本是否可见(自身事务修改的版本始终可见):
1.若 trx_id == creator_trx_id:
当前事务自己写的版本,可见
2.若 trx_id < min_trx_id:
该版本由 Read View 生成前已提交的事务创建,可见。
3.若 trx_id >= max_trx_id:
该版本由 Read View 生成后才启动的事务创建,不可见。
4.若 min_trx_id ≤ trx_id < max_trx_id:
若 trx_id 在 m_ids 中:生成该版本的事务仍活跃(未提交),不可见。
若 trx_id 不在 m_ids 中:生成该版本的事务已提交,可见。
四、总结
MVCC 通过版本链(基于 undo log 和隐藏列)和 Read View(可见性判断规则),实现了并发事务对数据的非阻塞读。其中,Read View 的四个字段定义了 "可见性边界",而不同隔离级别通过控制 Read View 的生成时机,平衡了并发性能与数据一致性(如 RR 的 "重复读" 与 RC 的 "实时读")。这种机制让 InnoDB 在高并发场景下,既能保证事务隔离性,又能避免读操作的锁阻塞,大幅提升了数据库性能。