题目:如何理解 MVCC?
难度:medium
标签:db/mysql/innodb, mysql/innodb, difficulty/medium
评分:⭐⭐☆☆(2/5)
MVCC 是在可重复读下的一个机制,它主要是用来控制,让不同的事务看到自己事务内的执行的内容,而不是看到其他事务,做到事务隔离的作用。它隔离的方式是通过版本控制,不同的事务来构成有自己各自的快照,看到自己当前版本下的快照的信息,而不会看到其他快照。
快照维护,我记得维护的数据信息是在哪个日志,具体没印象了。维护了包括当前的事务 ID,然后最大的活动的事务 ID,最小活动事务 ID,以及活动的事务 ID 的组。然后构建成它整个维护事务快照的一个数据。然后在它前面的事务,事务 ID 比它小的,它就不不去读,事务 ID 比它大的,事务 ID 比它小的它去读,事务 ID 比它大它不读。达到生成管控的不同事务间的隔离,进行版本并发控制。
优点:
min_trx_id、max_trx_id、m_ids(活动事务 ID 列表)不足:
trx_id、roll_pointer、delete_mark,这是 MVCC 的数据基础roll_pointer 串成链,ReadView 在链上找可见版本SELECT FOR UPDATE/UPDATE/DELETE)直接看最新提交版本补充:
trx_id(最后修改事务)、roll_pointer(指向 undo 上一条版本)、delete_mark(删除标记)核心考点定位:理解 MVCC 的数据结构基础(行版本 + undo 链)、ReadView 判定逻辑、以及它在隔离级别中的应用。
知识点拆解:
InnoDB 每行数据除了用户列外还有三个隐藏字段:
DB_TRX_ID(6 字节):最后一次修改这行的事务 IDDB_ROLL_PTR(7 字节):回滚指针,指向该行在 undo log 里的上一个版本DB_ROW_ID(6 字节):无主键表自动生成的行 IDDB_DELETE_MARK(1 字节):删除标记位DB_ROLL_PTR 指向 undo log 里的旧版本m_ids:创建 ReadView 时仍在活动的事务 ID 列表min_trx_id:m_ids 中最小的事务 IDmax_trx_id:创建 ReadView 时系统将要分配的下一个事务 ID(等于当前最大 + 1)creator_trx_id:创建这个 ReadView 的事务 ID(本身)给定一行数据的 DB_TRX_ID = trx_id,判断它对当前 ReadView 是否可见:
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 → 不可见(在活动事务里,可能未提交)trx_id ∉ m_ids → 可见(不在活动列表里说明已提交)DB_ROLL_PTR 找上一个版本,重复判断SELECT(不加 FOR UPDATE)SELECT FOR UPDATE、SELECT FOR SHARE、UPDATE、DELETEpurge 线程会删除已无引用的旧版本;长事务会阻碍 purge,undo log 膨胀考察方向:
延伸递进面试题:
SELECT FOR UPDATE 会走 MVCC 吗?面试官您好,MVCC 是 Multi-Version Concurrency Control,多版本并发控制,核心是让读和写互不阻塞。
底层结构有三部分:
第一是行的隐藏字段。InnoDB 每行数据除了用户列外还有三个隐藏字段:DB_TRX_ID 记录最后修改这行的事务 ID,DB_ROLL_PTR 是回滚指针,指向 undo log 里的上一个版本,DB_DELETE_MARK 是删除标记。
第二是 undo log 版本链。每次 UPDATE 都会生成一个新版本,同时把旧版本写入 undo log。反复 UPDATE 后一行数据就形成一条版本链:最新版本 → 上一版本 → 更早版本... 每次修改都往前挂一个版本。
第三是 ReadView。事务读数据时创建一个快照视图,里面有四个字段:m_ids 是当前还在活动的事务 ID 列表、min_trx_id 是 m_ids 里最小的、max_trx_id 是系统将要分配的下一个事务 ID、creator_trx_id 是创建者本身。
可见性判断是这样:拿一行数据的 trx_id 跟 ReadView 比:
如果当前版本不可见,就沿 DB_ROLL_PTR 找上一个版本继续判断,直到找到可见版本或者链尾。
RC 和 RR 的区别就是 ReadView 的生成时机不同。RC 每次 SELECT 都创建新的 ReadView,所以能看到其他事务提交后的最新值;RR 只在事务第一次 SELECT 时创建一次,事务期间一直复用,所以看不到其他事务期间提交的改动。这就是"RC 无法可重复读、RR 可以"的底层原因。
MVCC 只用于快照读,就是普通 SELECT。当前读——SELECT FOR UPDATE、UPDATE、DELETE——不走 MVCC,直接看最新的已提交版本,并通过锁保证一致性。
MVCC 还有个副作用要注意:undo log 链会随着版本累积变长。purge 线程会清理已无引用的旧版本,但长事务持有旧的 ReadView 会阻碍 purge,导致 undo log 膨胀。生产上要控制长事务的长度。