Amazon DocumentDB 迁移指南 #

一、迁移概述 #

1.1 迁移场景 #

text
常见迁移场景:
├── 从自建MongoDB迁移
├── 从MongoDB Atlas迁移
├── 从其他云服务迁移
├── 从其他数据库迁移
└── 数据中心迁移

1.2 迁移方式 #

text
迁移方式:
├── 在线迁移
│   ├── 使用DMS
│   └── 最小停机时间
│
├── 离线迁移
│   ├── 导出导入
│   └── 需要停机
│
└── 混合迁移
    ├── 全量+增量
    └── 平滑迁移

1.3 迁移流程 #

text
迁移流程:
├── 1. 评估和规划
├── 2. 兼容性检查
├── 3. 环境准备
├── 4. 数据迁移
├── 5. 应用适配
├── 6. 测试验证
├── 7. 切换上线
└── 8. 监控优化

二、迁移前评估 #

2.1 兼容性检查 #

javascript
// 检查MongoDB版本
db.version()

// DocumentDB支持版本:3.6, 4.0, 5.0
// 建议升级到兼容版本

// 检查使用的功能
db.adminCommand({ listCommands: 1 })

// 检查索引
db.collection.getIndexes()

// 检查数据大小
db.stats()

2.2 功能兼容性 #

text
DocumentDB兼容性:
├── 支持的功能
│   ├── CRUD操作
│   ├── 聚合管道
│   ├── 索引
│   ├── 事务(4.0+)
│   └── 变更流
│
└── 不支持/部分支持
    ├── 固定大小集合
    ├── 部分聚合操作符
    ├── MapReduce
    ├── GridFS
    └── 部分地理空间功能

2.3 数据评估 #

javascript
// 评估数据量
db.getCollectionNames().forEach(function(name) {
  const stats = db[name].stats();
  print(name + ": " + stats.size + " bytes, " + stats.count + " documents");
});

// 评估索引
db.getCollectionNames().forEach(function(name) {
  const indexes = db[name].getIndexes();
  print(name + ": " + indexes.length + " indexes");
});

三、环境准备 #

3.1 创建DocumentDB集群 #

bash
# 创建子网组
aws docdb create-db-subnet-group \
  --db-subnet-group-name my-subnet-group \
  --db-subnet-group-description "Migration subnet group" \
  --subnet-ids subnet-12345 subnet-67890

# 创建集群
aws docdb create-db-cluster \
  --db-cluster-identifier migration-cluster \
  --engine docdb \
  --engine-version 5.0.0 \
  --master-username admin \
  --master-user-password password123 \
  --db-subnet-group-name my-subnet-group \
  --vpc-security-group-ids sg-12345

# 创建实例
aws docdb create-db-instance \
  --db-instance-identifier migration-primary \
  --db-instance-class db.r6g.large \
  --engine docdb \
  --db-cluster-identifier migration-cluster

3.2 网络配置 #

text
网络要求:
├── 源数据库可访问
├── 目标DocumentDB可访问
├── 迁移服务器网络连通
├── 安全组规则配置
└── 带宽充足

3.3 迁移工具准备 #

bash
# 安装mongodump和mongorestore
# Ubuntu/Debian
sudo apt-get install mongodb-tools

# macOS
brew install mongodb-community-tools

# 安装AWS DMS
# 通过AWS控制台或CLI

四、数据迁移 #

4.1 使用mongodump/mongorestore #

bash
# 导出数据
mongodump \
  --host source-host \
  --port 27017 \
  --username admin \
  --password \
  --db mydb \
  --out /backup/dump

# 导入数据
mongorestore \
  --host target-cluster.cluster-xxx.docdb.amazonaws.com \
  --port 27017 \
  --username admin \
  --password \
  --ssl \
  --sslCAFile rds-combined-ca-bundle.pem \
  /backup/dump/mydb

# 并行导入(提高速度)
mongorestore \
  --host target-cluster.cluster-xxx.docdb.amazonaws.com \
  --port 27017 \
  --username admin \
  --password \
  --ssl \
  --sslCAFile rds-combined-ca-bundle.pem \
  --numInsertionWorkersPerCollection 8 \
  /backup/dump/mydb

4.2 使用mongoexport/mongoimport #

bash
# 导出为JSON
mongoexport \
  --host source-host \
  --port 27017 \
  --username admin \
  --password \
  --db mydb \
  --collection users \
  --out users.json

# 导入JSON
mongoimport \
  --host target-cluster.cluster-xxx.docdb.amazonaws.com \
  --port 27017 \
  --username admin \
  --password \
  --ssl \
  --sslCAFile rds-combined-ca-bundle.pem \
  --db mydb \
  --collection users \
  --file users.json

4.3 使用AWS DMS #

bash
# 创建复制实例
aws dms create-replication-instance \
  --replication-instance-identifier my-replication-instance \
  --replication-instance-class dms.r5.large \
  --allocated-storage 50

# 创建源端点
aws dms create-endpoint \
  --endpoint-identifier source-mongodb \
  --endpoint-type source \
  --engine-name mongodb \
  --server-name source-host \
  --port 27017 \
  --username admin \
  --password password123

# 创建目标端点
aws dms create-endpoint \
  --endpoint-identifier target-documentdb \
  --endpoint-type target \
  --engine-name docdb \
  --server-name target-cluster.cluster-xxx.docdb.amazonaws.com \
  --port 27017 \
  --username admin \
  --password password123

# 创建迁移任务
aws dms create-replication-task \
  --replication-task-identifier my-migration-task \
  --source-endpoint-arn source-endpoint-arn \
  --target-endpoint-arn target-endpoint-arn \
  --replication-instance-arn instance-arn \
  --migration-type full-load-and-cdc \
  --table-mappings '{
    "rules": [{
      "rule-type": "selection",
      "rule-id": "1",
      "object-locator": {
        "schema-name": "mydb",
        "table-name": "%"
      },
      "rule-action": "include"
    }]
  }'

4.4 脚本迁移 #

javascript
// 自定义迁移脚本
const { MongoClient } = require('mongodb');

async function migrateCollection(sourceUri, targetUri, dbName, collectionName, batchSize = 1000) {
  const sourceClient = new MongoClient(sourceUri);
  const targetClient = new MongoClient(targetUri, {
    tls: true,
    tlsCAFile: './rds-combined-ca-bundle.pem'
  });
  
  try {
    await sourceClient.connect();
    await targetClient.connect();
    
    const sourceCollection = sourceClient.db(dbName).collection(collectionName);
    const targetCollection = targetClient.db(dbName).collection(collectionName);
    
    let skip = 0;
    let total = 0;
    
    while (true) {
      const docs = await sourceCollection.find({}).skip(skip).limit(batchSize).toArray();
      
      if (docs.length === 0) break;
      
      await targetCollection.insertMany(docs, { ordered: false });
      
      skip += batchSize;
      total += docs.length;
      
      console.log(`Migrated ${total} documents`);
    }
    
    console.log(`Migration completed: ${total} documents`);
    
  } finally {
    await sourceClient.close();
    await targetClient.close();
  }
}

// 使用示例
migrateCollection(
  'mongodb://source-host:27017',
  'mongodb://target-cluster.cluster-xxx.docdb.amazonaws.com:27017',
  'mydb',
  'users'
);

五、索引迁移 #

5.1 导出索引定义 #

javascript
// 导出所有索引
db.getCollectionNames().forEach(function(collectionName) {
  print("Collection: " + collectionName);
  const indexes = db[collectionName].getIndexes();
  indexes.forEach(function(index) {
    printjson(index);
  });
});

5.2 创建索引 #

javascript
// 在目标数据库创建索引
// 根据导出的索引定义创建

// 单字段索引
db.users.createIndex({ email: 1 }, { unique: true });

// 复合索引
db.orders.createIndex({ userId: 1, createdAt: -1 });

// 文本索引
db.articles.createIndex({ content: "text" });

// TTL索引
db.sessions.createIndex(
  { createdAt: 1 },
  { expireAfterSeconds: 3600 }
);

六、应用适配 #

6.1 连接字符串修改 #

javascript
// 原连接字符串
const oldUri = "mongodb://user:pass@source-host:27017/mydb";

// 新连接字符串(DocumentDB)
const newUri = "mongodb://user:pass@target-cluster.cluster-xxx.docdb.amazonaws.com:27017/mydb?ssl=true&replicaSet=rs0";

// 完整配置
const client = new MongoClient(newUri, {
  tls: true,
  tlsCAFile: './rds-combined-ca-bundle.pem',
  replicaSet: 'rs0',
  readPreference: 'secondaryPreferred'
});

6.2 功能适配 #

javascript
// 检查不支持的功能
// 1. 固定大小集合
// MongoDB
db.createCollection("logs", { capped: true, size: 10000000 });

// DocumentDB替代方案
db.createCollection("logs");
// 使用TTL索引实现类似功能
db.logs.createIndex({ createdAt: 1 }, { expireAfterSeconds: 86400 });

// 2. MapReduce
// MongoDB
db.orders.mapReduce(mapFunction, reduceFunction, { out: "results" });

// DocumentDB替代方案
db.orders.aggregate([
  { $group: { _id: "$category", total: { $sum: "$amount" } } },
  { $out: "results" }
]);

6.3 驱动兼容性 #

text
驱动版本要求:
├── Node.js: mongodb >= 3.6
├── Python: pymongo >= 3.8
├── Java: mongodb-driver-sync >= 3.12
├── C#: MongoDB.Driver >= 2.9
└── Go: mongo-driver >= 1.3

七、测试验证 #

7.1 数据验证 #

javascript
// 验证文档数量
const sourceCount = await sourceCollection.countDocuments({});
const targetCount = await targetCollection.countDocuments({});
console.log(`Source: ${sourceCount}, Target: ${targetCount}`);

// 验证数据一致性
async function verifyData(sourceCollection, targetCollection) {
  const sourceDocs = await sourceCollection.find({}).sort({ _id: 1 }).toArray();
  const targetDocs = await targetCollection.find({}).sort({ _id: 1 }).toArray();
  
  if (sourceDocs.length !== targetDocs.length) {
    console.error('Document count mismatch');
    return false;
  }
  
  for (let i = 0; i < sourceDocs.length; i++) {
    const source = JSON.stringify(sourceDocs[i]);
    const target = JSON.stringify(targetDocs[i]);
    
    if (source !== target) {
      console.error(`Document mismatch at index ${i}`);
      return false;
    }
  }
  
  console.log('Data verification passed');
  return true;
}

7.2 功能测试 #

text
功能测试清单:
├── 连接测试
├── CRUD操作测试
├── 查询功能测试
├── 索引功能测试
├── 事务功能测试
├── 聚合功能测试
└── 应用集成测试

7.3 性能测试 #

javascript
// 性能对比测试
async function performanceTest(collection) {
  const start = Date.now();
  
  for (let i = 0; i < 1000; i++) {
    await collection.findOne({ _id: ObjectId() });
  }
  
  const duration = Date.now() - start;
  console.log(`1000 queries in ${duration}ms`);
}

八、切换上线 #

8.1 切换计划 #

text
切换步骤:
├── 1. 停止应用写入源数据库
├── 2. 执行最终数据同步
├── 3. 验证数据一致性
├── 4. 更新应用配置
├── 5. 重启应用服务
├── 6. 验证应用功能
├── 7. 监控系统状态
└── 8. 保留回滚方案

8.2 回滚方案 #

text
回滚准备:
├── 保留源数据库
├── 记录切换时间点
├── 准备快速回滚脚本
├── 通知相关人员
└── 制定回滚决策标准

九、迁移后优化 #

9.1 性能优化 #

text
迁移后优化:
├── 分析慢查询
├── 优化索引
├── 调整配置参数
├── 监控性能指标
└── 持续优化

9.2 成本优化 #

text
成本优化:
├── 选择合适的实例规格
├── 合理配置副本数量
├── 监控资源使用
├── 使用预留实例
└── 优化存储使用

十、常见问题 #

10.1 迁移速度慢 #

text
原因和解决:
├── 网络带宽限制:增加带宽
├── 迁移工具配置:优化并行度
├── 目标索引过多:先迁移后建索引
└── 资源不足:增加实例规格

10.2 数据不一致 #

text
原因和解决:
├── 迁移期间写入:停止写入或使用CDC
├── 网络问题:检查网络稳定性
├── 工具问题:验证迁移工具配置
└── 数据类型问题:检查数据类型兼容性

十一、总结 #

11.1 迁移要点 #

阶段 要点
评估 兼容性、数据量、功能
准备 环境、工具、网络
迁移 数据、索引、应用
验证 数据、功能、性能
切换 计划、验证、回滚

11.2 最佳实践总结 #

text
迁移最佳实践:
├── 充分评估和规划
├── 选择合适的迁移方式
├── 测试验证充分
├── 制定详细的切换计划
├── 保留回滚方案
└── 迁移后持续优化

下一步,让我们学习故障排除!

最后更新:2026-03-27