1. 当数据开始"旅行"时会发生什么?
就像快递员运输易碎品需要防震包装,我们在使用mongoexport/mongoimport这对"运输工具"时,稍不注意就会让数据"受伤"。想象我们要迁移一个用户评价系统,原始集合结构如下:
// 技术栈:MongoDB 5.0
// 评价集合结构示例
{
_id: ObjectId("5f3c8a9b12ecb724a8d4a7b3"),
productId: "SKU-2023", // 商品编号
ratings: [
{ userId: NumberLong(1234567890123), score: 4.5 }, // 注意Long类型
{ userId: NumberLong(2234567890123), score: 5.0 }
],
createdAt: ISODate("2023-01-01T08:00:00Z") // 标准日期类型
}
当使用基础导出命令时:
mongoexport --uri="mongodb://localhost:27017/reviews" \
--collection=productRatings \
--out=ratings.json
这份看似正常的导出文件里,藏着三个"暗礁": 1)所有日期字段变成字符串格式 2)Long型用户ID被转换为普通数字 3)嵌套数组中的类型信息完全丢失
2. 数据完整性"杀手"现形记
2.1 类型变异危机
当我们用普通JSON格式导出时,就像把精心包装的礼物拆成零件运输。日期类型变成"2023-01-01T08:00:00Z"这样的字符串,Long型数字可能丢失精度(超过53位时)。
2.2 索引消失事件
假设原集合有复合索引:
db.productRatings.createIndex({productId:1, createdAt:-1})
直接导入新环境时,如果不重建索引,查询速度会从跑车变牛车。更危险的是唯一索引缺失可能导致重复数据。
3.3 幽灵数据困境
在不停机迁移时,旧系统持续写入可能导致:
# 导出过程中新增的文档(编号004)不会被包含
已导出文档:001 -> 002 -> 003
系统继续写入:004
最终导入文档:001-003
这种数据缺口就像搬家时漏装了一个箱子,初期难以察觉但后患无穷。
3. 数据护卫队的三道防线
3.1 类型保全方案
使用扩展JSON模式保留数据类型:
mongoexport --uri="mongodb://localhost:27017/reviews" \
--collection=productRatings \
--jsonFormat=canonical \ # 关键参数!保留BSON类型
--out=ratings_bson.json
导出的文档会变成:
{
"_id": {"$oid": "5f3c8a9b12ecb724a8d4a7b3"},
"createdAt": {"$date": "2023-01-01T08:00:00Z"},
"ratings": [{
"userId": {"$numberLong": "1234567890123"},
"score": 4.5
}]
}
3.2 索引重建策略
导入完成后立即重建索引:
# 先导入数据
mongoimport --uri="mongodb://newhost:27017/reviews" \
--collection=productRatings \
--file=ratings_bson.json
# 连接新数据库建立索引
mongosh "mongodb://newhost:27017/reviews" <<EOF
db.productRatings.createIndex({productId:1, createdAt:-1},
{background: true}) # 后台建索引不影响服务
EOF
3.3 数据一致性组合拳
对于关键业务数据,使用OPLOG追踪:
# 使用mongodump进行热备份
mongodump --uri="mongodb://localhost:27017/reviews" \
--oplog # 记录操作日志
--out=/data/backup
# 恢复时重放日志
mongorestore --uri="mongodb://newhost:27017/reviews" \
--oplogReplay \
/dump
4. 工具选型指南与避坑宝典
应用场景对照表
场景 | 推荐工具 | 优势 | 陷阱预警 |
---|---|---|---|
开发环境数据样本导出 | mongoexport | 快速生成测试数据 | 丢失索引和分片信息 |
生产环境全量迁移 | mongodump | 保留索引和集合属性 | 需要停机维护窗口 |
跨版本升级 | bsondump | 处理不同版本兼容性问题 | 操作流程复杂 |
实时数据同步 | 变更流+自定义程序 | 实现秒级同步 | 开发维护成本高 |
必知禁忌清单
- 禁止在未测试的情况下直接操作生产数据(建议先用
--dryRun
参数) - 警惕数字类型的"53位魔咒":超过2^53的整数必须使用Long类型
- 时区陷阱:日期字段导入时要确认服务端时区设置
- 内存杀手:大集合导出时添加
--quiet
参数避免日志撑爆内存
救命校验脚本
导入完成后运行这个快速检查:
// 在Mongo Shell中执行
const oldStats = db.getSiblingDB('reviews_old').stats();
const newStats = db.getSiblingDB('reviews_new').stats();
print('文档数对比:');
printjson({ old: oldStats.collections.productRatings.count,
new: newStats.collections.productRatings.count });
print('索引数验证:');
const oldIndexes = db.getSiblingDB('reviews_old')
.productRatings.getIndexes().length;
const newIndexes = db.getSiblingDB('reviews_new')
.productRatings.getIndexes().length;
print(`旧环境索引数:${oldIndexes},新环境索引数:${newIndexes}`);
5. 数据搬运大师的自我修养
经过多次"血泪教训",我总结出可靠迁移的三要素:合适的工具选择、严格的操作流程、完善的回滚方案。记住以下几个黄金法则:
1)大容量数据迁移前,先用1%的数据做全流程测试
2)永远在操作前执行db.fsyncLock()
冻结写入
3)保留三份备份:操作前、操作中、操作后
4)使用jq
命令预处理JSON文件,提前过滤异常数据
下次当您需要移动数据时,不妨先做个深呼吸,检查这份清单。毕竟,数据世界的每一趟旅程,都应该是精心规划的护航行动,而不是充满风险的冒险之旅。