MVCC 面试口述稿 · PostgreSQL 八股
整体理解非常到位!以下保留原始说话逻辑,修正小口误,去掉卡顿,标出原文几处小漏洞,直接照着说即可。
原文小勘误
- MVCC 全称:多版本并发控制,不是多并发版本控制(口误颠倒)
- 更新出来新 tuple:
xmax=0,0不是代表事务活跃,是哨兵——没有被删除/修改过 - 可见性判断:不是只满足两个条件,要组合判断;
xmin必须提交合格之后,才去看xmax
面试口述版(完整)
PostgreSQL 的 MVCC 叫多版本并发控制,目的是实现读写不互相阻塞。
PG 里面一行逻辑数据,物理上叫 tuple 元组。每个 tuple 会自带两个隐藏列:xmin 和 xmax。
xmin:生成这个版本的事务 ID,插入或者更新产生新版本的时候赋值。xmax:让这个版本过期、删除的事务 ID;xmax=0是特殊标记,代表这个版本还没有被任何人删除或者更新。
PG 修改数据不会原地覆盖,是追加新版本。
当我更新一行的时候,不会改动原来的旧元组:
- 旧 tuple,把它的
xmax设置为当前更新事务的 ID,旧版本就被标记待过期,但不会立刻删掉,仍然留在堆表里。 - 再生成一条全新的 tuple,新版本的
xmin就是本次更新的事务 ID,xmax赋值 0。
这样堆表里就同时存在多个物理 tuple,代表同一行逻辑数据,历史版本全部留在堆中。
每个事务开启的时候会拿到一份快照,快照记录当下所有正在活跃、还没提交的事务 ID。读数据的时候,扫描每一个物理 tuple,结合快照做可见性判断:
首先看 xmin:这个版本是谁创建的。如果 xmin 对应的事务还活跃没提交,这个版本直接不可见;只有 xmin 事务已经提交,才有资格继续判断。
再看 xmax:
- 如果
xmax=0:没有事务来删除它,那就可见。 - 如果
xmax是一个真实事务 ID:xmax对应的事务还活跃没提交:代表删除/更新动作还没有生效,这个 tuple 依旧可见。xmax对应的事务已经提交:代表这个版本正式作废,不可见。
这些死掉、不再被任何事务看见的死元组,不会自动回收,需要 Vacuum 来清理。如果有长时间运行的老事务,Vacuum 就不能清理死元组,就会出现表膨胀。
极简版(1 分钟,适合时间很短的面试)
PG 的 MVCC 是多版本并发控制,读写互不阻塞。
每一个物理元组 tuple 有隐藏的 xmin、xmax。更新不原地修改,而是追加新版本。旧元组填上 xmax 标记过期,新旧版本全部留在堆表。
事务拥有快照,记录当前活跃事务。判断可见性:先看 xmin,生成版本的事务必须提交;再看 xmax,如果干掉它的事务已经提交,版本就作废。
废弃的死元组靠 Vacuum 清理,长事务会阻止 Vacuum,造成表膨胀。PG 没有 undo log,历史版本存在堆表里,这是和 InnoDB 很大的区别。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 風間花雨!
