MySQL 单点详解(06)

MySQL 单点详解(06):一条记录为什么有多个版本,ReadView 到底在比什么

面试官:MVCC 是怎么实现的?

面试官:RR 和 RC 差在哪里,底层只差一行代码吗?

MVCC 解决的不是并发,是"读的时候不用等写"。

一条记录能同时是老样子和新样子,靠的是版本链加一个快照。

一、版本链是怎么长出来的

InnoDB 的每一行,除了建表时写的那几列,还藏着两个看不见的列。

表① 藏起来的两个列

列 作用
事务号 最后改这一行的事务是哪个
回滚指针 指向 undo 里这个值的上一个版本

改一次,就多一个版本。旧值不进数据页,进 undo。

举个具体的:余额原本 100,被事务 8 改成 200。数据页里是 200,undo 里留着 100。

回滚指针从 200 那一版,指向 100 那一版。

回滚指针一根一根往回串,串成一条链。链头是现在,链尾是最早。

第五期讲过 undo 记"改之前",这里就是它的用处:不只是回滚,还给读提供历史版本。

所以一条记录能同时是"现在的样子"和"三分钟前的样子",就靠这条链。

二、ReadView 是个什么快照

有了链,还得知道该停在哪一节。

ReadView 就是那张车票:生成它的那一刻,谁已经提交、谁还在跑,全记下来。

表② 快照里记了四个值

值 含义
活跃事务列表 生成快照时还没提交的事务号
最小值 上面那堆里最小的那个
上限 下一个要发出去的事务号
自己 当前这个事务的号

四个值一摆,"该看哪一版"就变成一道比较题,不需要再猜。

三、可见性就三步判断

拿链上某个版本的事务号,跟快照比。

表③ 三个判断,五种结果

事务号的情况 结论
小于最小值 早就提交了,可见
大于等于上限 快照之后才开的,不可见
夹在中间,且在活跃列表里 当时还没提交,不可见
夹在中间,但不在列表里 已经提交,可见
等于自己 自己改的,可见

不可见就顺着指针往回走一格,再比一次。一直走到可见为止。

比如快照里活跃列表是 8 和 9,最小值 8,上限 12。

链上某一版的事务号是 7,比 8 小,直接可见。是 10,落在中间又不在列表里,说明它已经提交,同样可见。

只有 8 和 9 这两个号,以及大于等于 12 的,才要往回翻。

这就是"读不阻塞写"的全部秘密:读的是一份历史,写的是最新一版,两边不抢同一把锁。

四、RC 和 RR 只差一个时机

表④ 两个隔离级别,同一套机制

项 RC RR
快照什么时候建 每条语句建一次 事务里第一次读建一次
同一事务读两次 可能不一样 一定一样

机制完全一样,只是快照的寿命不同。

RC 每条语句都重新拍一张,所以能看到别人刚提交的数据,这就是不可重复读。

RR 一张票用到事务结束,所以两次读到的永远一致。

顺带分清一对容易混的概念:快照读看见的是历史,当前读看见的是现实,两者在同一个事务里可以并存。

面试里问"RR 怎么实现的",答"事务开始时建一个 ReadView,全程复用",一句就够。

五、面试官会往下问的三层

第一层:MVCC 能解决幻读吗?

只能解决一半。普通 select 走快照,看不见新插入的行,这半是解决了。

但加锁读、update、delete 读的是最新版本,照样能看见别人新插的行。那半靠间隙锁,下一期讲。

所以说 RR 不是"完全没有幻读",准确说法是"快照读没有幻读"。

第二层:既然有版本链,为什么还要加锁?

因为写和写之间没法用版本解决。两个事务改同一行,谁赢?

版本链只服务读,写要靠锁排队。

第三层:旧版本什么时候能删?

等没有快照还需要它的时候。系统会记住最老的活跃快照,比它还老的版本才能清掉。

所以一个长事务会拖住整个库的清理,undo 越堆越大,磁盘也跟着涨。

收尾

这一期可以压成一句话:改的时候往 undo 里留一份旧的,读的时候拿快照比一比,比到可见的那一版为止。

相关推荐
CodeHackerBhx1 小时前
MySQL 迁达梦踩坑实录:FIND_IN_SET、timestamp(6) 和一整批要改的函数
数据库·mysql·达梦·信创·数据库迁移
lqbzlll1 小时前
Spring Boot 开启虚拟线程后接口反而变慢了?4 个排查方向和对应修法
java·spring boot·后端
n 55!w !1081 小时前
MySQL笔记整理2
数据库·笔记·mysql
aiks_yuan1 小时前
MySQL 批量插入很慢怎么办:rewriteBatchedStatements 实测 10 万行从 4.5 秒到 1 秒(JDBC、MyBatis、LOAD DATA 对比)
mysql
七夜zippoe1 小时前
LangChain4j vs Spring AI:Java AI 框架怎么选(对比表)
java·人工智能·spring·ai·langchain4j
悠仁さん1 小时前
【C++】set map 使用
java·前端·c++
李纲明2 小时前
WordPress 提示「建立数据库连接时出错」:先核 wp-config 与数据库服务,再查表损坏与磁盘配额
数据库·wordpress·devdiy
yuniko-n2 小时前
【JUC】Thread 内存泄露
java
freejackman2 小时前
【架构篇】双遍历 + 多栈协同:手撸 SQL 血缘引擎的架构思考
java·大数据·数据库·hive·sql·架构·spark