3.2 Manifest 与版本管理


3.2 Manifest 与版本管理:状态的记账体系

本节摘要:磁盘上有几百个 SST 文件,哪个是现行的、哪个已作废、哪个层正在压缩——这些「数据库此刻长什么样」的问题,答案只存在于一处:Manifest 账本。本节讲清四位主角的分工:账本 Manifest、内存里的现行版本视图、变更修正案 VersionEdit、以及作为生效印章的指针文件 CURRENT。看完你会明白,单机状态一致性可以不靠锁和协议,只靠文件系统的两个原子保证。

一位审计师的直觉题

假设你是审计师,面对这样的现场:目录里堆着一千个 SST 文件,没有说明书,进程崩溃了。你的任务是重建「崩溃前一毫秒数据库的完整逻辑状态」。从哪里下手?

直觉会告诉你:需要一份流水账,记录每个文件的生死簿——何时被登记为有效、何时被宣告作废。只要这份流水账本身可靠(写入原子、损坏可识别),现场就能重建。RocksDB 的版本管理体系就是把这份直觉工程化:Manifest 是流水账,VersionSet 是账本在内存里的现行汇总,VersionEdit 是一条条修正分录,CURRENT 是封面上写着的「以哪一本账为准」。 四个角色,一套完整的记账制度。

Manifest:单点真相源

Manifest 是一个只追加的日志文件,地位至高无上:数据库逻辑视图的唯一权威。SST 文件本身只是数据的躯壳,没有账本的背书,磁盘上的文件什么都不是——一个不在账本上的 SST 等同于垃圾,一个被账本宣告作废的 SST 等同于已删除。这条「账本即现实」的原则,是理解恢复流程的钥匙。

追加式记账带来三个直接的好处。写入便宜:一次压缩完成,账本只多几十到几百字节的分录,不用重写全账。崩溃安全:半截分录带着坏的校验码,重放时自动作废,账本仍然自洽。历史可追溯:账本从头读一遍,数据库的每一次结构变更尽收眼底——调 bug 和做审计时,这是一座金矿。

VersionSet 与 VersionEdit:现行视图与修正案

VersionSet 是账本的内存镜像,维护着「现行版本」:每一层有哪些文件、每个文件的键范围与序列号范围。它采用不可变视图的管理方式——从不修改现行版本,只创建新版本。所有读操作拿着现行版本的引用干活,切换发生在指针一闪念之间,读者毫无察觉。

VersionEdit 是修正案的标准格式,只描述最原子的元数据操作:登记哪些新文件、作废哪些旧文件、层的边界如何变化。它不包含任何键值内容,纯粹是元数据的记账语言。这个设计的扩展性极好:新的功能只需要在分录里增加新字段、在应用逻辑里加一个处理分支,账本的骨架从不动摇。

一次版本跃迁的完整流程值得细看,这是全节最核心的机制:

后台压缩完成,构造一份修正案 → 提交给版本集合 → 版本集合在内存里把修正案套在现行版本上,生成候选新版本 → 一致性校验(键范围不得越界、作废的文件必须真实存在、序列号必须单调)→ 校验通过,指针原子切换到新版本 → 修正案写入 Manifest 并落盘 → 等待中的线程被唤醒。

注意两处安排的深意。先切换内存指针,再写账本:读路径零成本拿到新视图,旧视图因读者还握着引用而安全存活到读完为止。账本写入失败则整个跃迁失败:宁可回滚这次压缩的成果,也不能让内存与账本出现两个版本的现实。

图3-2 版本管理的记账体系

图3-2 版本管理的记账体系

CURRENT:一行的宪法

版本账本本身也会换代:账本太大会轮换新文件。哪本是现行的?答案写在一个内容只有一行的文件里——CURRENT。别看它小,它承担的是「生效印章」的职责:启动恢复时,引擎只信它指向的那本账。

印章的更换利用了文件系统最可靠的原语:重命名的原子性。新账本写好、刷盘、然后一步重命名顶替旧的 CURRENT。无论何时断电,CURRENT 要么是旧的、要么是新的,绝不存在半新半旧。一个看似复杂的状态一致性问题,就这样被降维成操作系统最基础的两个保证——追加写的完整性(校验码兜底)与重命名的原子性(文件系统背书)。没有锁、没有协调者、没有协议。

恢复流程:把记账制度走一遍

现在可以完整回答本节开头的审计题了。崩溃重启后,引擎依次做四件事:

  1. 读 CURRENT,拿到现行账本的文件名——信任从这一行开始;
  2. 从头重放账本的全部分录,跳过所有校验码不符的半截记录,重建出版本集合;
  3. 扫描数据目录,把「磁盘上存在但账本上没有」的文件识别为孤儿,清进垃圾堆;
  4. 依据账本里记录的日志进度,重放预写日志,把崩溃前留在内存里的数据补回来。

四步走完,逻辑状态与崩溃前分毫不差。你在运维中可以利用这套制度的两个推论:备份的最小单位是「全部 SST 加账本加印章」,缺一不可;日志分析是排错的金路子——账本原文就是数据库结构变更的完整编年史,哪次压缩登记了哪些文件、哪次刷盘作废了哪些文件,历历在目。

账本自身的卫生问题

账本记的是文件的生灭,它自己会不会越长越大?会。每次压缩、每次刷盘都追加修正案,账本日志单调增长——放在多年不关机的实例上,这是不可忽视的体量。引擎的自洁手段与文件的回收同源:账本定期自我归并——把累积的全部修正案折叠成一份「当前完整清单」,写进新账本文件,再用指针文件一次切换,旧账本整体退役。整个过程与压缩一样依靠「先写新、后改名、再清旧」的老规矩,切换期间读账的路径由内存中的现行版本承担,前台无感。

卫生问题的另一面是半截记录。账本与数据日志一样按记录追写,崩溃可能留下半条修正案。这里再次用到校验码自证:重放账本时,校验不过的记录及其之后的内容一律作废——因为后续修正案可能依赖这条残缺记录的前置状态。账本的截断点就是状态的回退点:实例会回到最后一次完整修正案时的文件清单,被跳过的那几笔变更里涉及的文件,要么重新生成、要么作为孤儿在后续清理中退役。这个性质决定了排错时的一个直觉:实例「丢」了最近的某些文件变化时,第一件事是核对账本的完整重放进行到哪一条。

本节要点

  • 账本即现实:SST 文件的效力完全由 Manifest 背书,不在账上的文件等同垃圾;
  • 修正案是纯元数据的记账语言,账本骨架因此十年不动摇;
  • 版本切换的次序是「先内存指针、后账本落盘」,读者永远看见自洽的完整版本;
  • CURRENT 用文件重命名的原子性担当生效印章,单机状态一致性被降维成两个文件系统保证;
  • 恢复四步:读印章、重放账本、清理孤儿、重放日志;
  • 备份最小单位与排错金路子都源自这套记账制度。

记账体系就位。本章还剩最后一块拼图:当多种负载同住一个实例时,如何划分领地互不拖累——下一节的列族实战。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U