如果 MongoDB 单库数据量超过几百 GB,使用 mongodump 做全量备份时,导出阶段可能要跑几十分钟甚至数小时,恢复时逐条重建索引的时间更长。物理备份的思路是绕开集合遍历,直接复制 dbPath 下的底层数据文件,让备份时间更接近磁盘复制速度。虽然执行路径短,但对文件一致性和锁机制的要求并不低。下面围绕 WiredTiger 引擎,从停止实例的冷备份、在线加锁复制、文件系统快照以及恢复校验几个角度拆解。

一、先厘清物理备份与逻辑备份的差异
逻辑备份通常使用 mongodump,它会把集合中的文档逐条读取出来并写成 BSON 文件,恢复时再用 mongorestore 把文档重新插入。这种方式的优点是跨版本兼容性较好,可以选择只备份部分库表,甚至可以在不同存储引擎之间迁移。但缺点也很明显:当文档数量达到亿级时,逻辑备份会消耗大量 CPU 和内存,恢复时间动辄数倍于备份时间。
物理备份则直接复制 MongoDB 数据目录。对 WiredTiger 引擎来说,一个数据目录通常包含 WiredTiger.turtle、WiredTiger.wt、_mdb_catalog.wt、collection-*.wt、index-*.wt、storage.bson、journal 子目录等文件。把这些文件完整复制到备份位置,恢复时再放回 dbPath,MongoDB 启动后就能直接识别数据。由于不需要逐条解析文档,物理备份吞吐量更接近磁盘复制能力,适合数据量大且对恢复时间敏感的场景。
但物理备份也有明显限制。它要求备份端和恢复端使用基本一致的 MongoDB 版本和存储引擎,尤其是主版本差异过大会导致数据目录无法直接加载。此外,在线物理备份必须解决文件一致性问题,否则复制过程中可能拿到一个写入到一半的集合文件,恢复后出现损坏。
二、停止服务后直接拷贝数据目录:冷备份
冷备份是最稳妥的物理备份方式。它的核心是先将 mongod 进程正常停止,确保已提交的写入全部刷入磁盘,并且没有任何新写入发生,然后再复制整个 dbPath。这样得到的文件在时间点上是一致的,只要目录结构完整,恢复时基本不会遇到存储引擎层面的损坏。
Linux 环境下,假设 MongoDB 数据目录为 /var/lib/mongo,备份目录为 /backup/mongo_full,可以先停服务,再用 cp -a 保留文件权限和目录结构进行复制。
# 停止 MongoDB 服务 systemctl stop mongod # 创建带日期的备份目录 mkdir -p /backup/mongo_full_$(date +%F) # 复制整个数据目录,保留权限和软链接 cp -a /var/lib/mongo/. /backup/mongo_full_$(date +%F)/ # 确认备份目录大小 du -sh /backup/mongo_full_$(date +%F)
Windows 下可以使用 robocopy 或 xcopy。如果 MongoDB 安装在 C:\MongoDB,数据目录是 C:\MongoDB\data,可以先用服务管理器停止 MongoDB,再执行复制。注意 robocopy 的退出码小于 8 都代表有文件被正常复制,不一定是失败。
net stop MongoDB robocopy "C:\MongoDB\data" "D:\Backup\mongo_data" /E /COPY:DAT /DCOPY:DAT /R:3 /W:5
冷备份的不足也一目了然:服务必须停机。对于核心业务库,每次备份都停库显然不现实。即便在低峰期执行,如果数据量达到 TB 级,复制时间超过维护窗口,也会影响业务。因此还需要掌握在线状态下的物理备份方式。
三、不中断业务的物理备份:fsync 加锁与快照
生产环境中更常用的是在线物理备份。MongoDB 提供了 db.fsyncLock() 命令,它会强制将内存中的脏页写入磁盘,并获取全局锁阻止后续写入。锁住实例后,数据文件暂时处于稳定状态,此时复制数据目录就能得到一致备份。复制完成后必须调用 db.fsyncUnlock() 解除锁,否则写请求会一直阻塞。
使用 mongosh 或旧的 mongo shell 都可以执行这两个命令。下面的示例先加锁,复制完成后解锁。复制期间业务如果只读还能继续,写入会被挂起,因此不适合锁太久。
# 对 MongoDB 实例加全局写锁 mongosh --quiet --eval "db.fsyncLock()" # 锁住后复制数据目录 cp -a /var/lib/mongo/. /backup/mongo_hot_$(date +%F)/ # 复制完成后解锁 mongosh --quiet --eval "db.fsyncUnlock()"
如果单靠 fsync 锁的时间过长,可以配合文件系统快照。以 LVM 为例,先加锁,然后创建逻辑卷快照,之后立即解锁,再从快照卷中复制文件。这样业务写阻塞时间只持续到快照创建完成,通常是秒级。ZFS、EBS 快照等也适用类似思路。
# 加锁 mongosh --quiet --eval "db.fsyncLock()" # 创建 LVM 快照 lvcreate --size 20G --snapshot --name mongo_snap /dev/vgdata/mongolv # 解锁 mongosh --quiet --eval "db.fsyncUnlock()" # 挂载快照后复制 mount /dev/vgdata/mongo_snap /mnt/mongo_snap cp -a /mnt/mongo_snap/. /backup/mongo_snap_backup/ umount /mnt/mongo_snap lvremove -f /dev/vgdata/mongo_snap
需要注意,文件系统快照必须保证是原子级别的。普通目录复制即便在加锁状态下也未必绝对安全,因为某些文件可能在复制过程中被修改,而文件系统快照可以避免这类问题。对于云主机或虚拟机,直接对磁盘做快照也是同理。
四、从物理备份恢复:目录回填与启动校验
恢复物理备份的第一步同样是停止目标实例。接着把备份目录中的内容完整放回 dbPath,并保证 MongoDB 进程拥有读写权限。Linux 下常见的错误是直接用 root 复制,结果启动 mongod 时因为文件属主错误而失败,所以复制后需要执行 chown -R mongod:mongod /var/lib/mongo。
# 停止服务 systemctl stop mongod # 把旧的数据库目录移到一旁,避免误删 mv /var/lib/mongo /var/lib/mongo_old # 将备份复制回 dbPath cp -a /backup/mongo_full_20250101/. /var/lib/mongo/ # 修正属主和权限 chown -R mongod:mongod /var/lib/mongo chmod 750 /var/lib/mongo # 启动服务 systemctl start mongod
启动后不要只看服务状态,还要进入数据库确认存储引擎和数据目录是否正常。可以执行 db.serverStatus() 查看 storageEngine.name,再用 db.stats() 检查各库对象数量。对于核心集合,可以抽样统计文档数量,确认与备份前业务侧记录一致。
mongosh --quiet --eval "db.serverStatus().storageEngine"
mongosh --quiet --eval "db.adminCommand({ listDatabases: 1 })"如果启动时报错,优先检查 mongod.log 的最后几十行。常见原因包括备份目录不完整、缺少 WiredTiger.turtle 或 WiredTiger.wt、文件权限错误、跨版本恢复导致 catalog 不兼容等。定位到具体文件后,再针对性补拷或修正权限,通常可以解决大部分恢复失败问题。
五、容易踩中的几个坑
第一个坑是只复制 collection-*.wt 和 index-*.wt,认为这些就是集合数据。实际上 WiredTiger.wt、WiredTiger.turtle、_mdb_catalog.wt、storage.bson、journal 等文件同样关键,缺任何一个都可能导致 WiredTiger 无法打开数据库。物理备份必须复制整个 dbPath。
第二个坑是直接把正在运行实例的数据目录复制过去。即便使用普通 cp 命令没有报错,也不代表文件一致。若集合文件在复制过程中发生写入,恢复后可能只恢复一半索引,启动阶段或查询时出现校验失败。因此在线备份必须加锁或使用快照,不能裸拷。
第三个坑是忽略了 MongoDB 版本兼容性。物理备份不是逻辑导出,不能像 mongodump 那样在不同主版本之间随意恢复。尤其是 Mongod 大版本升级后,WiredTiger 文件格式和 catalog 结构可能变化。恢复环境的 MongoDB 版本最好与备份时完全一致,至少也要在同一个主版本内进行小版本升级。
第四个坑是备份文件没有验证。很多人做完备份就放在那里,等出问题时才发现目录不完整或文件损坏。可以在备份完成后计算关键文件的 MD5 或记录目录大小,恢复前先做一次一致性检查。对于重要实例,还可以隔一段时间做一次恢复演练,确认备份真正可用。
MongoDB物理备份数据文件拷贝WiredTiger修改时间:2026-10-05 04:01:53