本节摘要:磁盘上有几百个 SST 文件,哪个是现行的、哪个已作废、哪个层正在压缩——这些「数据库此刻长什么样」的问题,答案只存在于一处:Manifest 账本。本节讲清四位主角的分工:账本 Manifest、内存里的现行版本视图、变更修正案 VersionEdit、以及作为生效印章的指针文件 CURRENT。看完你会明白,单机状态一致性可以不靠锁和协议,只靠文件系统的两个原子保证。
假设你是审计师,面对这样的现场:目录里堆着一千个 SST 文件,没有说明书,进程崩溃了。你的任务是重建「崩溃前一毫秒数据库的完整逻辑状态」。从哪里下手?
直觉会告诉你:需要一份流水账,记录每个文件的生死簿——何时被登记为有效、何时被宣告作废。只要这份流水账本身可靠(写入原子、损坏可识别),现场就能重建。RocksDB 的版本管理体系就是把这份直觉工程化:Manifest 是流水账,VersionSet 是账本在内存里的现行汇总,VersionEdit 是一条条修正分录,CURRENT 是封面上写着的「以哪一本账为准」。 四个角色,一套完整的记账制度。
Manifest 是一个只追加的日志文件,地位至高无上:数据库逻辑视图的唯一权威。SST 文件本身只是数据的躯壳,没有账本的背书,磁盘上的文件什么都不是——一个不在账本上的 SST 等同于垃圾,一个被账本宣告作废的 SST 等同于已删除。这条「账本即现实」的原则,是理解恢复流程的钥匙。
追加式记账带来三个直接的好处。写入便宜:一次压缩完成,账本只多几十到几百字节的分录,不用重写全账。崩溃安全:半截分录带着坏的校验码,重放时自动作废,账本仍然自洽。历史可追溯:账本从头读一遍,数据库的每一次结构变更尽收眼底——调 bug 和做审计时,这是一座金矿。
VersionSet 是账本的内存镜像,维护着「现行版本」:每一层有哪些文件、每个文件的键范围与序列号范围。它采用不可变视图的管理方式——从不修改现行版本,只创建新版本。所有读操作拿着现行版本的引用干活,切换发生在指针一闪念之间,读者毫无察觉。
VersionEdit 是修正案的标准格式,只描述最原子的元数据操作:登记哪些新文件、作废哪些旧文件、层的边界如何变化。它不包含任何键值内容,纯粹是元数据的记账语言。这个设计的扩展性极好:新的功能只需要在分录里增加新字段、在应用逻辑里加一个处理分支,账本的骨架从不动摇。
一次版本跃迁的完整流程值得细看,这是全节最核心的机制:
后台压缩完成,构造一份修正案 → 提交给版本集合 → 版本集合在内存里把修正案套在现行版本上,生成候选新版本 → 一致性校验(键范围不得越界、作废的文件必须真实存在、序列号必须单调)→ 校验通过,指针原子切换到新版本 → 修正案写入 Manifest 并落盘 → 等待中的线程被唤醒。
注意两处安排的深意。先切换内存指针,再写账本:读路径零成本拿到新视图,旧视图因读者还握着引用而安全存活到读完为止。账本写入失败则整个跃迁失败:宁可回滚这次压缩的成果,也不能让内存与账本出现两个版本的现实。

版本账本本身也会换代:账本太大会轮换新文件。哪本是现行的?答案写在一个内容只有一行的文件里——CURRENT。别看它小,它承担的是「生效印章」的职责:启动恢复时,引擎只信它指向的那本账。
印章的更换利用了文件系统最可靠的原语:重命名的原子性。新账本写好、刷盘、然后一步重命名顶替旧的 CURRENT。无论何时断电,CURRENT 要么是旧的、要么是新的,绝不存在半新半旧。一个看似复杂的状态一致性问题,就这样被降维成操作系统最基础的两个保证——追加写的完整性(校验码兜底)与重命名的原子性(文件系统背书)。没有锁、没有协调者、没有协议。
现在可以完整回答本节开头的审计题了。崩溃重启后,引擎依次做四件事:
四步走完,逻辑状态与崩溃前分毫不差。你在运维中可以利用这套制度的两个推论:备份的最小单位是「全部 SST 加账本加印章」,缺一不可;日志分析是排错的金路子——账本原文就是数据库结构变更的完整编年史,哪次压缩登记了哪些文件、哪次刷盘作废了哪些文件,历历在目。
账本记的是文件的生灭,它自己会不会越长越大?会。每次压缩、每次刷盘都追加修正案,账本日志单调增长——放在多年不关机的实例上,这是不可忽视的体量。引擎的自洁手段与文件的回收同源:账本定期自我归并——把累积的全部修正案折叠成一份「当前完整清单」,写进新账本文件,再用指针文件一次切换,旧账本整体退役。整个过程与压缩一样依靠「先写新、后改名、再清旧」的老规矩,切换期间读账的路径由内存中的现行版本承担,前台无感。
卫生问题的另一面是半截记录。账本与数据日志一样按记录追写,崩溃可能留下半条修正案。这里再次用到校验码自证:重放账本时,校验不过的记录及其之后的内容一律作废——因为后续修正案可能依赖这条残缺记录的前置状态。账本的截断点就是状态的回退点:实例会回到最后一次完整修正案时的文件清单,被跳过的那几笔变更里涉及的文件,要么重新生成、要么作为孤儿在后续清理中退役。这个性质决定了排错时的一个直觉:实例「丢」了最近的某些文件变化时,第一件事是核对账本的完整重放进行到哪一条。
记账体系就位。本章还剩最后一块拼图:当多种负载同住一个实例时,如何划分领地互不拖累——下一节的列族实战。