2. MongoDB存储引擎与持久化机制

要理解 MongoDB 的备份恢复,不能只停留在 mongodump 和 mongorestore 的命令层面,而必须看清数据在磁盘上究竟以什么形态存在、哪些文件是“真相”、哪些文件只是“过程记录”。MongoDB 默认使用 WiredTiger 存储引擎,它把数据组织在数据文件、journal 日志、检查点以及复制集特有的 oplog 之中。这四类对象承担着完全不同的职责,也决定了不同备份方式的边界。

一、WiredTiger 的磁盘布局:数据文件与检查点

在 WiredTiger 中,每个集合和索引默认各自对应一个独立的表文件,存放在数据库目录下,命名形如 collection-*.wt 与 index-*.wt。这些 .wt 文件才是数据的最终归属地。但 WiredTiger 并不会在每次写入时立刻把页写回这些文件,而是先在内存缓存中修改,再通过检查点(checkpoint)机制把某一时刻的一致快照刷写到磁盘。

检查点可以理解为“给整个存储引擎拍一张带时间戳的全量快照”。执行检查点时,WiredTiger 会把内存中所有脏页落盘,并在元数据中记录一个 checkpoint 条目,指向该时刻各数据文件的有效版本。MongoDB 默认大约每 60 秒触发一次检查点,也可由 storage.wiredTiger.engineConfig.checkpointSizeMB 等参数影响其行为。检查点完成后,磁盘上的数据文件就代表了一个自洽的、可被恢复的起点。

关键点在于:检查点不是实时的。两次检查点之间发生的写入,只存在于内存和 journal 中。如果此时断电,数据文件还停留在上一个检查点,最新写入尚未进入 .wt 文件。

二、journal 日志:检查点之间的“补丁”

为了弥补检查点的时间窗口,WiredTiger 引入了 journal(日志)。journal 采用预写日志(WAL)思想:任何修改在写入数据文件之前,先以追加方式写入 journal 文件。MongoDB 默认开启 journal,日志文件位于 journal/ 目录,单个文件约 100MB,写满后轮转。

journal 记录的是“逻辑上如何修改”,而不是完整的数据页。它让 MongoDB 在崩溃后能够重放(replay)自上一个检查点以来尚未落盘的操作,把数据文件推进到崩溃前的一致状态。因此恢复流程可以概括为:先加载最近一次检查点,再重放 journal 中该检查点之后的记录。

journal 保证的是单节点崩溃一致性,它不解决磁盘损坏、误删集合或跨节点复制的问题。备份策略必须建立在“journal 只能救崩溃、不能救误操作”的认知之上。

三、oplog:复制集层面的操作流水

在复制集中,主节点会把所有影响数据的写操作记录到一个特殊的 capped collection——local.oplog.rs 中,这就是 oplog。oplog 是幂等的、可重放的逻辑操作序列,从节点通过持续拉取并应用 oplog 来保持与主节点一致。

oplog 与 journal 容易混淆,但二者层次不同:journal 是存储引擎内部的物理/逻辑日志,服务于单节点崩溃恢复;oplog 是复制集层面的逻辑操作日志,服务于节点间数据同步。oplog 有固定大小(由 replSetResizeOplog 或启动参数决定),是环形覆盖的,旧记录会被新记录挤出。这意味着 oplog 只能覆盖一个有限的时间窗口,超出窗口的从节点将无法增量同步,必须重新做全量初始同步。

四、四类对象的关系与备份含义

把上述对象放在一起,就能看清备份恢复的底层对象关系:数据文件是最终状态,检查点是数据文件的某个一致快照点,journal 是检查点之后到崩溃前的增量补丁,oplog 则是复制集维度上可被其他节点重放的变更流。下图展示了它们在同一节点上的层次与数据流向。

WiredTiger 持久化对象与数据流向 客户端写操作 insert / update / delete WiredTiger 内存缓存 脏页 / 修改暂存 journal 日志 WAL 预写日志 崩溃恢复重放 先写日志 检查点 checkpoint 约每 60s 触发 一致快照落盘 刷脏页 数据文件 .wt collection-*.wt index-*.wt 检查点写入 oplog.rs local 库 capped 复制集同步流 写操作记录 从节点 secondary 拉取并重放 oplog 崩溃恢复路径 ① 加载最近检查点 → 数据文件 ② 重放 journal 中检查点后记录 ③ 达到崩溃前一致状态

从备份视角看,这张图给出了三条重要结论。第一,物理文件级备份(如文件系统快照、mongodump --oplog 之外的冷备)必须保证数据文件与 journal 的一致性,否则恢复时可能缺少检查点之后的增量。第二,逻辑备份(mongodump)导出的是 BSON 文档,它绕过了存储引擎细节,但无法保留 oplog 的精确时间点,因此复制集场景下需要配合 --oplog 才能获得某个时间点的一致性快照。第三,oplog 是增量恢复与时间点恢复(PITR)的关键,只要 oplog 窗口覆盖目标时间点,就可以从全量备份出发,重放 oplog 到指定时刻。

五、参数与观察点

在实际运维中,可以通过以下方式观察这些对象的状态。查看检查点与 journal 相关统计:

db.serverStatus().wiredTiger
db.serverStatus().wiredTiger.log   // journal 相关指标
db.serverStatus().oplog            // 复制集下查看 oplog 窗口
rs.printReplicationInfo()          // 查看 oplog 大小与时间跨度

其中 rs.printReplicationInfo() 会输出 oplog 的已用时间窗口,例如“configured oplog size: 2048MB,log length start to end: 3600secs”,这直接决定了 PITR 能回溯多远。若窗口只有一小时,那么任何超过一小时的恢复目标都无法仅靠 oplog 完成。

不要把 journal 与 oplog 混为一谈:journal 是单节点存储引擎的崩溃恢复日志,oplog 是复制集的逻辑同步日志。备份方案中,journal 影响的是“文件是否可恢复”,oplog 影响的是“能恢复到哪个时间点”。

理解这四类对象的层次与流向,后续选择冷备、热备、逻辑备份还是快照备份时,才能准确判断每种方式覆盖了哪些对象、遗漏了哪些对象,以及恢复时究竟能回到哪一个一致状态。