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 处理不同版本兼容性问题 操作流程复杂
实时数据同步 变更流+自定义程序 实现秒级同步 开发维护成本高

必知禁忌清单

  1. 禁止在未测试的情况下直接操作生产数据(建议先用--dryRun参数)
  2. 警惕数字类型的"53位魔咒":超过2^53的整数必须使用Long类型
  3. 时区陷阱:日期字段导入时要确认服务端时区设置
  4. 内存杀手:大集合导出时添加--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文件,提前过滤异常数据

下次当您需要移动数据时,不妨先做个深呼吸,检查这份清单。毕竟,数据世界的每一趟旅程,都应该是精心规划的护航行动,而不是充满风险的冒险之旅。