MongoDB 从本地迁移到云端,不能只考虑数据文件复制。云数据库通常屏蔽了底层文件系统,同时要求 TLS 连接、强认证和最小权限,因此逻辑备份与恢复是最稳妥的通用方案。但这并不意味着只能选择全量停机。根据库内数据量、写入频率和可接受的停机时间,可以组合出至少三种迁移策略:离线备份恢复、带 oplog 的时间点恢复、基于 Change Streams 的准实时同步。下面从评估开始,逐层拆解。

一、迁移前评估:版本、网络、权限与容量
第一步是核对 MongoDB 版本。云数据库一般支持 4.2、4.4、5.0、6.0、7.0 等主流大版本,但未必开放所有小版本。如果本地是 3.6 或更早版本,直接迁移可能遇到驱动兼容和聚合语法问题。建议先在测试库执行 mongodump 和 mongorestore 验证,不要等到生产切换时才发现 $merge 或 allowDiskUse 等用法不被支持。
网络连通性决定迁移工具的选择。如果云 MongoDB 只允许内网访问,迁移机需要部署在同一个 VPC 或通过专线打通;如果需要公网迁移,必须为源库出口 IP 配置白名单,并尽量启用 TLS。可以使用 mongosh 做一次连通性测试:
mongosh "mongodb+srv://user:password@your-cluster.mongodb.net/admin" \
--eval "db.runCommand({ connectionStatus: 1 })"
这里的反斜杠表示命令换行,执行时如果不想换行,可以去掉反斜杠后合并为一行。连接串中的账号需要具备 readWrite 或更高权限,目标库侧则需要 restore、dbAdmin 等权限。云厂商通常不允许用户直接创建 admin 库下的系统集合,因此导入时要控制命名空间。
容量评估也很关键。除了文档大小,还要计算索引空间。目标库如果开启自动扩容,可以提高写入吞吐;如果使用固定存储,需要确保磁盘容量至少为源库数据量加索引量的 1.2 倍。对单个集合超过 1TB 的库,建议拆分集合并行迁移,避免单连接重放瓶颈。
二、离线迁移:全量导出、恢复与一致性检查
离线迁移适合数据量不大或业务允许数小时停机的场景。核心命令是 mongodump 和 mongorestore。先从本地导出完整库:
mongodump \ --host 127.0.0.1 \ --port 27017 \ --username backup_user \ --password 'strong_pwd' \ --authenticationDatabase admin \ --out /data/backup/mongo_local
--out 指定输出目录,默认会为每个数据库生成一个子目录,每个集合生成 .bson 和 .metadata.json 文件。如果只迁移部分集合,可以添加 --db=orders --collection=orders_archive 之类的过滤参数。导出时建议使用 --oplog 参数记录导出开始后的增量操作,这样恢复时可以回放到同一时间点:
mongodump \ --host 127.0.0.1:27017 \ --username backup_user \ --password 'strong_pwd' \ --authenticationDatabase admin \ --oplog \ --out /data/backup/mongo_local_oplog
恢复到云 MongoDB 时,使用 mongorestore。如果目标库是新库,建议保留 --drop 参数,先删除同名集合再写入,避免重复导入时主键冲突。MongoDB 4.4 及以上版本会自动识别并重放 dump 中包含的 oplog,旧版本则需要手动添加 --oplogReplay。
mongorestore \ --uri "mongodb+srv://user:password@your-cluster.mongodb.net" \ --drop \ /data/backup/mongo_local_oplog
恢复完成后不要马上切换业务,先做一致性抽查。可以统计关键集合的文档数、校验分片键字段是否完整、执行简单聚合查询。例如比较源库和目标库的 db.orders.countDocuments() 与 db.orders.aggregate([{ $group: { _id: "$status", n: { $sum: 1 } } }]) 结果。不要在验证阶段直接删除旧库,保留至少一个完整备份周期。
三、低停机迁移:oplog 与 Change Streams 的配合
生产环境往往只允许分钟级或秒级停机。此时可以先做一次全量备份,然后持续消费源库的变更,把增量数据追到目标库。MongoDB 的副本集通过 oplog 记录写操作,mongodump --oplog 导出的时间点之后,可以在迁移程序里读取 Change Streams 获取后续变更。Change Streams 基于副本集 oplog,但对开发者更友好,支持过滤和重试。
一个简化版的 Python 同步脚本如下,它监听源库的 orders 集合并把 insert、update、replace 事件写入目标库:
from pymongo import MongoClient
src = MongoClient("mongodb://user:pass@127.0.0.1:27017/?replicaSet=rs0&authSource=admin")
dst = MongoClient("mongodb+srv://user:pass@your-cluster.mongodb.net")
with src.watch([{"$match": {"ns.coll": "orders"}}]) as stream:
for change in stream:
ns = change["ns"]
coll = dst[ns["db"]][ns["coll"]]
op = change["operationType"]
if op == "insert":
coll.insert_one(change["fullDocument"])
elif op in ("update", "replace"):
doc_id = change["documentKey"]["_id"]
coll.replace_one({"_id": doc_id}, change["fullDocument"], upsert=True)
elif op == "delete":
doc_id = change["documentKey"]["_id"]
coll.delete_one({"_id": doc_id})
该同步脚本只适合演示,生产环境要处理乱序、重复消费和字段变更事件,例如 update 事件可能只有 updateDescription 而没有完整文档,需要监听 fullDocument=updateLookup 或使用 fullDocumentBeforeChange 对比。
真正切换时,流程通常是:停止业务写入、等待同步程序追平增量、切换连接串到云端、恢复写入。追平判断可以使用来源库与目标库最近一条 oplog 时间戳对比,也可以直接停写后观察同步延迟。对于 MongoDB 6.0 及以上版本,可以使用 $changeStream 的 startAtOperationTime 精确续传。
四、迁移后验证与常见坑
切换连接串后并不能立即宣告完成。需要跑一轮全链路校验:文档数、索引数量、慢查询表现、读写延迟。索引不会随 mongorestore 自动完全保持原样,尤其是部分索引、TTL 索引和文本索引参数可能在旧版本与云端版本存在差异。建议在恢复完成后执行 listIndexes 对比源库与目标库的索引定义,必要时手动重建:
mongosh "mongodb+srv://user:password@your-cluster.mongodb.net/orders" \
--eval "db.orders.createIndex({ user_id: 1, created_at: -1 }, { name: 'idx_user_created' })"
另一个常见问题是连接串切换时没有修改读偏好。云 MongoDB 副本集通常有主节点和从节点,应用程序如果沿用本地单节点写法,可能把读请求打到主节点,导致 CPU 偏高。应确认 readPreference=secondaryPreferred 或根据业务需求设置。对分片集群,还要确认分片键字段是否在查询条件中,否则云端跨分片查询会明显变慢。
最后是安全与回滚。迁移完成后,旧库不要立即下线,至少保留一到两周,并限制旧库写入。若云库出现不可预期问题,可以快速回切连接串。连接串建议放在配置中心或环境变量中,不要在代码仓库硬编码。云数据库的备份策略、监控告警和磁盘自动扩容也要在切换前开启,避免迁移完成后因备份任务抢占资源导致峰值延迟。