一、数据迁移概述
1.1 什么是MongoDB数据迁移
MongoDB数据迁移是指将数据库中的数据从一个环境转移到另一个环境的过程。常见场景包括:服务器升级、数据中心迁移、开发环境到生产环境的部署、分片集群扩容等。数据迁移需要保证数据完整性和一致性,同时尽量减少业务中断时间。
1.2 迁移方式对比
| 方式 | 适用场景 | 停机时间 | 数据量 | 复杂度 |
|---|---|---|---|---|
| mongodump/mongorestore | 小型数据库 | 较长 | <100GB | 低 |
| mongoexport/mongoimport | 跨格式迁移 | 较长 | <50GB | 低 |
| 副本集同步 | 热迁移 | 极短 | 不限 | 中 |
| 文件系统复制 | 同环境迁移 | 中等 | 不限 | 中 |
| mongosync | 分片集群 | 极短 | 不限 | 高 |
二、迁移前准备
2.1 环境检查
# 源环境检查
mongosh --eval "db.adminCommand({ buildInfo: 1 })" | grep version
mongosh --eval "db.stats()" | grep dataSize
df -h /var/lib/mongodb
# 目标环境检查
mongosh --eval "db.adminCommand({ buildInfo: 1 })" | grep version
df -h /var/lib/mongodb
# 网络连通性测试
ping target-server
telnet target-server 27017
2.2 数据量评估
# 查看数据库大小
mongosh --eval "
db.adminCommand('listDatabases').databases.forEach(function(db) {
print(db.name + ': ' + Math.round(db.sizeOnDisk / 1024 / 1024) + ' MB');
});
"
# 查看集合大小
mongosh mydb --eval "
db.getCollectionNames().forEach(function(col) {
print(col + ': ' + Math.round(db[col].stats().size / 1024 / 1024) + ' MB');
});
"
# 查看索引大小
mongosh mydb --eval "
db.getCollectionNames().forEach(function(col) {
print(col + ' indexes: ' + Math.round(db[col].stats().totalIndexSize / 1024 / 1024) + ' MB');
});
"
2.3 创建备份
# 全量备份
mongodump --uri="mongodb://admin:Password@source-server:27017" \
--authenticationDatabase=admin \
--gzip \
--archive=/backup/mongo_pre_migration_$(date +%Y%m%d).archive
# 验证备份
mongorestore --gzip --archive=/backup/mongo_pre_migration_20260514.archive \
--nsInclude='test.*' --dryRun
三、mongodump/mongorestore迁移
3.1 导出数据
# 导出所有数据库
mongodump --uri="mongodb://admin:Password@source-server:27017" \
--authenticationDatabase=admin \
--gzip \
--out=/backup/mongo_dump_$(date +%Y%m%d)
# 导出指定数据库
mongodump --uri="mongodb://admin:Password@source-server:27017/mydb" \
--authenticationDatabase=admin \
--gzip \
--out=/backup/mydb_dump
# 导出指定集合
mongodump --uri="mongodb://admin:Password@source-server:27017/mydb" \
--authenticationDatabase=admin \
--gzip \
--collection=users \
--out=/backup/users_dump
# 导出并排除指定集合
mongodump --uri="mongodb://admin:Password@source-server:27017/mydb" \
--authenticationDatabase=admin \
--gzip \
--excludeCollection=logs \
--excludeCollection=temp_data \
--out=/backup/mydb_no_logs
3.2 传输数据
# 使用rsync传输(推荐,支持断点续传)
rsync -avz --progress /backup/mongo_dump_20260514/ user@target-server:/backup/mongo_dump/
# 使用scp传输
scp -r /backup/mongo_dump_20260514/ user@target-server:/backup/mongo_dump/
# 压缩后传输(节省带宽)
tar czf mongo_dump.tar.gz /backup/mongo_dump_20260514/
scp mongo_dump.tar.gz user@target-server:/backup/
ssh user@target-server "cd /backup && tar xzf mongo_dump.tar.gz"
3.3 导入数据
# 导入所有数据库
mongorestore --uri="mongodb://admin:Password@target-server:27017" \
--authenticationDatabase=admin \
--gzip \
/backup/mongo_dump/
# 导入指定数据库
mongorestore --uri="mongodb://admin:Password@target-server:27017" \
--authenticationDatabase=admin \
--gzip \
--nsInclude="mydb.*" \
/backup/mongo_dump/
# 导入并覆盖已有数据
mongorestore --uri="mongodb://admin:Password@target-server:27017" \
--authenticationDatabase=admin \
--gzip \
--drop \
/backup/mongo_dump/
# 并行导入(加速大数据量)
mongorestore --uri="mongodb://admin:Password@target-server:27017" \
--authenticationDatabase=admin \
--gzip \
--numParallelCollections=4 \
/backup/mongo_dump/
四、mongoexport/mongoimport迁移
4.1 导出为JSON/CSV
# 导出为JSON格式
mongoexport --uri="mongodb://admin:Password@source-server:27017/mydb" \
--authenticationDatabase=admin \
--collection=users \
--out=/backup/users.json
# 导出为CSV格式
mongoexport --uri="mongodb://admin:Password@source-server:27017/mydb" \
--authenticationDatabase=admin \
--collection=users \
--type=csv \
--fields="_id,name,email,created_at" \
--out=/backup/users.csv
# 导出查询结果
mongoexport --uri="mongodb://admin:Password@source-server:27017/mydb" \
--authenticationDatabase=admin \
--collection=users \
--query='{"status": "active"}' \
--out=/backup/active_users.json
4.2 导入JSON/CSV
# 导入JSON
mongoimport --uri="mongodb://admin:Password@target-server:27017/mydb" \
--authenticationDatabase=admin \
--collection=users \
--file=/backup/users.json
# 导入CSV
mongoimport --uri="mongodb://admin:Password@target-server:27017/mydb" \
--authenticationDatabase=admin \
--collection=users \
--type=csv \
--headerline \
--file=/backup/users.csv
# 导入并指定upsert
mongoimport --uri="mongodb://admin:Password@target-server:27017/mydb" \
--authenticationDatabase=admin \
--collection=users \
--mode=upsert \
--upsertFields=email \
--file=/backup/users.json
五、副本集热迁移
5.1 热迁移原理
通过在目标服务器上添加副本集成员,让MongoDB自动同步数据,同步完成后进行主节点切换,实现几乎零停机的数据迁移。
5.2 添加新节点
在源主节点执行:
// 查看当前副本集配置
rs.conf()
// 添加新节点
rs.add("target-server:27017")
// 查看同步状态
rs.status()
// 等待同步完成
// 检查optime时间戳是否一致
5.3 监控同步进度
// 在新节点检查同步状态
rs.status().members.forEach(function(m) {
print(m.name + " - state: " + m.stateStr + " - optime: " + m.optime.ts);
})
// 检查复制延迟
db.getReplicationInfo()
5.4 切换主节点
// 确认新节点数据同步完成
rs.status()
// 将新节点提升为主节点
rs.stepDown() // 在当前主节点执行
// 或者指定新主节点
rs.freeze(120) // 在旧主节点冻结120秒
rs.stepDown(120) // 在旧主节点让出主节点
5.5 移除旧节点
// 在新主节点移除旧节点
rs.remove("old-server:27017")
// 验证配置
rs.conf()
六、分片集群迁移
6.1 迁移Config Server
# 1. 停止Mongos路由
sudo systemctl stop mongos
# 2. 备份Config Server数据
mongodump --uri="mongodb://source-config:27019" \
--gzip \
--out=/backup/config_dump
# 3. 在新Config Server上恢复
mongorestore --uri="mongodb://target-config:27019" \
--gzip \
/backup/config_dump
# 4. 更新Mongos配置指向新Config Server
# 编辑/etc/mongos.conf,更新configDB字段
# 5. 重启Mongos
sudo systemctl start mongos
6.2 迁移Shard节点
# 1. 在Mongos上添加新Shard
sh.addShard("shard_new_rs/target-shard1:27018")
# 2. 等待数据均衡
sh.isBalancerRunning()
sh.status()
# 3. 移除旧Shard
sh.removeShard("shard_old_rs")
# 4. 验证迁移完成
sh.status()
七、数据验证
7.1 数据完整性验证
# 对比文档数量
mongosh source-server --eval "db.users.countDocuments({})" --quiet
mongosh target-server --eval "db.users.countDocuments({})" --quiet
# 对比集合大小
mongosh source-server --eval "db.users.stats().size" --quiet
mongosh target-server --eval "db.users.stats().size" --quiet
# 验证索引
mongosh source-server --eval "db.users.getIndexes()" --quiet
mongosh target-server --eval "db.users.getIndexes()" --quiet
7.2 抽样验证
// 随机抽样验证
function verifyData(sourceUri, targetUri, db, col, sampleSize) {
const sourceConn = new Mongo(sourceUri);
const targetConn = new Mongo(targetUri);
const sourceDb = sourceConn.getDB(db);
const targetDb = targetConn.getDB(db);
const samples = sourceDb[col].aggregate([
{ $sample: { size: sampleSize } }
]).toArray();
let match = 0;
let mismatch = 0;
samples.forEach(function(doc) {
const target = targetDb[col].findOne({ _id: doc._id });
if (target && JSON.stringify(doc) === JSON.stringify(target)) {
match++;
} else {
mismatch++;
print("Mismatch: " + doc._id);
}
});
print("匹配: " + match + ", 不匹配: " + mismatch);
}
verifyData("source-server:27017", "target-server:27017", "mydb", "users", 1000);
7.3 校验和验证
# 使用mongodump的dryRun验证
mongorestore --gzip --archive=/backup/migration.archive --dryRun
# 数据校验脚本
mongosh --eval "
db.runCommand({ dbHash: 1 });
"
八、回滚方案
8.1 回滚准备
迁移前必须准备回滚方案:
– 保留源环境数据直到验证完成
– 记录迁移开始时间点
– 准备应用层切换方案
8.2 执行回滚
# 1. 停止目标环境写入
# 2. 重新指向源环境
# 3. 恢复源环境在迁移期间的增量数据
mongorestore --gzip --archive=/backup/incremental.archive \
--nsInclude="mydb.*" \
--noIndexRestore
# 4. 验证数据
mongosh source-server --eval "db.users.countDocuments({})"
九、性能优化
9.1 加速导出
# 使用多线程导出
mongodump --uri="mongodb://admin:Password@source:27017" \
--numParallelCollections=4 \
--gzip \
--out=/backup/dump
# 排除不需要的集合
mongodump --uri="mongodb://admin:Password@source:27017/mydb" \
--excludeCollection=audit_logs \
--excludeCollection=temp_sessions \
--gzip \
--out=/backup/dump
9.2 加速导入
# 并行导入
mongorestore --uri="mongodb://admin:Password@target:27017" \
--numParallelCollections=4 \
--batchSize=100 \
--gzip \
/backup/dump
# 先导入数据再建索引
mongorestore --uri="mongodb://admin:Password@target:27017" \
--noIndexRestore \
--gzip \
/backup/dump
# 数据导入完成后手动建索引
mongosh target-server --eval "
db.users.createIndex({ email: 1 }, { unique: true });
db.orders.createIndex({ user_id: 1, created_at: -1 });
"
十、自动化迁移脚本
#!/bin/bash
# /usr/local/bin/mongo-migrate.sh
SOURCE_URI="mongodb://admin:Password@source:27017"
TARGET_URI="mongodb://admin:Password@target:27017"
BACKUP_DIR="/backup/migration_$(date +%Y%m%d_%H%M%S)"
LOG_FILE="/var/log/mongo-migration.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a $LOG_FILE
}
log "开始MongoDB数据迁移..."
# 1. 创建备份目录
mkdir -p $BACKUP_DIR
log "备份目录: $BACKUP_DIR"
# 2. 导出数据
log "开始导出数据..."
mongodump --uri="$SOURCE_URI" \
--authenticationDatabase=admin \
--gzip \
--out=$BACKUP_DIR/dump
if [ $? -ne 0 ]; then
log "导出失败,终止迁移"
exit 1
fi
log "导出完成"
# 3. 传输数据
log "开始传输数据..."
rsync -avz --progress $BACKUP_DIR/dump/ user@target-server:$BACKUP_DIR/dump/
if [ $? -ne 0 ]; then
log "传输失败,终止迁移"
exit 1
fi
log "传输完成"
# 4. 导入数据
log "开始导入数据..."
mongorestore --uri="$TARGET_URI" \
--authenticationDatabase=admin \
--gzip \
--drop \
$BACKUP_DIR/dump/
if [ $? -ne 0 ]; then
log "导入失败,需要回滚"
exit 1
fi
log "导入完成"
# 5. 验证数据
log "开始验证数据..."
SOURCE_COUNT=$(mongosh "$SOURCE_URI/mydb" --eval "db.users.countDocuments({})" --quiet)
TARGET_COUNT=$(mongosh "$TARGET_URI/mydb" --eval "db.users.countDocuments({})" --quiet)
if [ "$SOURCE_COUNT" -eq "$TARGET_COUNT" ]; then
log "验证通过: 源=$SOURCE_COUNT, 目标=$TARGET_COUNT"
else
log "验证失败: 源=$SOURCE_COUNT, 目标=$TARGET_COUNT"
fi
log "迁移完成"
总结
MongoDB数据迁移是一项需要谨慎操作的任务,关键要点:
- 充分准备:评估数据量、测试网络、创建备份
- 选择合适的迁移方式:小数据量用mongodump,大数据量用副本集同步
- 验证数据:迁移后必须验证数据完整性和一致性
- 准备回滚方案:保留源数据直到验证完成
- 尽量减少停机时间:使用热迁移方案,在低峰期执行
注:本文基于MongoDB 7.0和Debian 12编写,生产环境迁移前请在测试环境验证。