跳过正文

华为云 DDS 迁移到天翼云 DDS:基于 mongodump / mongorestore 的实战指南

Greatfinish
作者
Greatfinish
记录 Oracle、PostgreSQL、达梦、Linux、存储与生产环境故障处理经验。
目录
20260423_The_Eiffel_Tower.png

一、项目背景与方案选择
#

在 MongoDB 兼容数据库的跨云迁移场景中,常见方案主要有两类:

  • 基于复制服务的全量 + 增量迁移
  • 基于 mongodump / mongorestore 的逻辑全量迁移

本文记录的是一次 “华为云 DDS → 天翼云 DDS” 的跨云迁移实践。综合源端与目标端约束、本次数据规模、停机窗口和实施复杂度,最终采用了 基于 mongodump / mongorestore 的逻辑全量迁移方案

  1. 在华为云 DDS 源端执行 mongodump --archive --gzip 导出
  2. 在天翼云 DDS 目标端执行 mongorestore --archive --gzip 恢复
  3. 通过集合、索引和关键对象核验迁移结果

该方案的优点是实施简单、操作透明、适合一次性全量切换;需要注意的是,它通常要求停写窗口,不适用于严格零停机迁移。

二、迁移前必须明确的约束
#

1. 源端约束:华为云 DDS
#

在基于 mongodump / mongorestore 的迁移方式下,源端需重点关注以下边界条件:

  • 该方案本质上是全量迁移
  • 为保证一致性,迁移前应尽量停止源端业务写入
  • 建议在业务低峰期执行导出与切换
  • 系统库不应作为迁移对象,不支持迁移系统库 adminlocal
  • 客户端工具版本应与实例版本保持兼容,根据华为云DDS 版本进行工具下载,本次我们迁移的DDS 4.2/4.0版本,用到链接验证工具MongoDB Shell下载对应的版本,MongoDB Command Line Database Tools Download导出导入工具下载对应的版本

此外,在实际执行过程中,如果导出在接近结束时失败,还应优先检查执行主机的磁盘空间是否充足,并预留必要冗余。

2. 目标端约束:天翼云 DDS
#

目标端的关键约束同样需要在迁移前确认:

  • 不支持高版本向低版本迁移
  • 不支持系统库迁移
  • 用户和角色需要在目标端手动创建
  • 业务对象迁移的核心范围通常为:集合、索引、视图

这意味着,即使源端导出归档中包含了 admin.system.usersadmin.system.roles 等系统对象,恢复到目标端时也不应直接恢复系统库内容。目标端所需账号、权限和角色,应通过平台能力或手工方式补齐。

三、迁移环境说明
#

1. 源端环境
#

本次实操中,源端为华为云 DDS 文档数据库实例,其中本文以 dds-fros 为例进行演示。

华为云DDS.png

使用 mongosh 连接源端:

mongosh "mongodb://127.0.0.1:27017"
mongosh "mongodb://user:password@127.0.0.1:27017/admin"
mongosh --host 127.0.0.1 --port 27017

在云数据库环境中,通常可直接使用具备读写权限的迁移账号(比如rwuser用户)连接实例。本文实际连接示例如下:

root@gkfx-sz01p-ecs-uat-his-ecs-dev:~# mongosh "mongodb://rwuser:*******@123.249.113.188:8635/admin?authSource=admin"
Current Mongosh Log ID: 69ea1a7775987a754645bcba
Connecting to:          mongodb://<credentials>@123.249.113.188:8635/admin?authSource=admin&directConnection=true&appName=mongosh+1.10.6
Using MongoDB:          4.2.0
Using Mongosh:          1.10.6
mongosh 2.8.2 is available for download: https://www.mongodb.com/try/download/shell

For mongosh info see: https://docs.mongodb.com/mongodb-shell/

[direct: mongos] admin> 

查看数据库列表:

[direct: mongos] admin> show dbs
admin              262 B
ccm                257 B
ccm2           27.40 MiB
config        356.12 KiB
dms           419.56 MiB
dms-test      384.22 MiB
form-builder       269 B
freehealth    303.78 KiB
fros           16.24 KiB
fros-test      19.30 KiB
gyyy               262 B
stream             260 B
test               267 B
[direct: mongos] admin> 

查看 ccm2 数据库中的集合:

[direct: mongos] admin> use ccm2
switched to db ccm2
[direct: mongos] ccm2> show collections
_Idempotency
_Join:city:Province
_Join:disease:TagDefinition
_Join:diseaseManagement_:Patient
_Join:diseaseManagementDefinition_:Organization
_Join:district:City
_Join:planDefinition:DiseaseManagementDefinition
_Join:role:PractitionerRole
_Join:task:DiseaseManagement
_Role
_SCHEMA
_Session
_User
BasicInfoDefinition
City
CodeableConcept
Disease
DiseaseManagement
DiseaseManagementDefinition
DiseaseManagementIndicator
District
Doctor
Game
Generate_MongoLastResumeData
Generate_PatientObservation
Indicator
IndicatorDefinition
Location
Observation
Organization
Patient
PatientIndicator
PatientIndicatorLog
PlanDefinition
Practitioner
PractitionerRole
Province
Questionnaire
QuestionnaireResponse
RemoteStudy
Report
Reservation
RoleDefinition
Room
Schedule
Sms
Tag
TagDefinition
Task
Test
system.profile
[direct: mongos] ccm2> 

为了便于迁移后核验,建议在源端提前记录每个业务库的集合数量。本文使用以下脚本统计各库集合数:

const conn = db.getMongo();

conn.getDBs().databases.forEach(({ name }) => {
  const curDb = conn.getDB(name);
  const collCount = curDb.getCollectionInfos(
    { type: "collection" },
    { nameOnly: true }
  ).length;

  print(`${name}: ${collCount}`);
});

源端统计结果如下:

[direct: mongos] ccm2> use admin
switched to db admin
[direct: mongos] admin> const conn = db.getMongo();

[direct: mongos] admin> 

[direct: mongos] admin> conn.getDBs().databases.forEach(({ name }) => {
...   const curDb = conn.getDB(name);
...   const collCount = curDb.getCollectionInfos(
...     { type: "collection" },
...     { nameOnly: true }
...   ).length;
... 
...   print(`${name}: ${collCount}`);
... });
admin: 5
ccm: 2
ccm2: 51
config: 14
dms: 53
dms-test: 53
form-builder: 7
freehealth: 3
fros: 11
fros-test: 2
gyyy: 6
stream: 3
test: 5

[direct: mongos] admin> 

建议将需要迁移的业务库集合数量提前保存,作为迁移后核验的基础指标。

2. 目标端环境
#

目标端为天翼云 DDS 文档数据库实例 GKFX-SZP-DDS-PDT-HIS-dds-fros。本文场景中,目标端启用了 TLS/SSL 连接,因此在客户端侧需要准备服务端证书文件。

天翼云DDS.png

注意我们这里目标环境是开启SSL,所以要下载服务端证书,传到客户端进行校验

天翼云DDS_SSL.png

实际连接目标端的命令如下:

root@gkfx-sz01p-ecs-uat-his-ecs-dev:~# mongosh "mongodb://rwuser:******@10.190.18.168:8635/admin?authSource=admin" --tls --tlsAllowInvalidHostnames --tlsCAFile /root/dds_bak/ca-dds.crt
Current Mongosh Log ID: 69ea102264e5cab4ea53a766
Connecting to:          mongodb://<credentials>@10.190.18.168:8635/admin?authSource=admin&directConnection=true&tls=true&tlsAllowInvalidHostnames=true&tlsCAFile=%2Froot%2Fdds_bak%2Fca-dds.crt&appName=mongosh+1.10.6
Using MongoDB:          4.2.0
Using Mongosh:          1.10.6
mongosh 2.8.2 is available for download: https://www.mongodb.com/try/download/shell

For mongosh info see: https://docs.mongodb.com/mongodb-shell/

[direct: mongos] admin> 

查看目标端数据库列表:

[direct: mongos] admin> show dbs
admin              262 B
config         38.58 KiB
[direct: mongos] admin> 

注意 本文环境中使用了 --tlsAllowInvalidHostnames,原因是服务端证书主机名与实际连接地址不完全匹配。该参数适合临时验证或受控环境排障使用;在正式生产中,更推荐使用与证书一致的域名进行连接,避免长期依赖放宽校验参数。

四、源端导出:执行 mongodump
#

1. 导出命令
#

本文源端使用 mongodump --archive --gzip 进行整实例逻辑导出。实际执行如下:

root@gkfx-sz01p-ecs-uat-his-ecs-dev:~# mongodump --version
mongodump version: 100.12.2
git version: f76a3ae4029780f61c49cbd39b7336f8d9c30ed0
Go version: go1.23.8
   os: linux
   arch: amd64
   compiler: gc
root@gkfx-sz01p-ecs-uat-his-ecs-dev:~# cd dds_bak/
root@gkfx-sz01p-ecs-uat-his-ecs-dev:~/dds_bak# mongodump --uri="mongodb://rwuser:******@123.249.113.188:8635/?authSource=admin" --archive=dds-fros_full_20260423.gz --gzip >> dds-fros_full_20260423.log 2>&1
 

该方式的优点是:

  • 生成单一归档文件,便于管理
  • 配合 --gzip 可降低归档体积
  • 适合跨环境传输与恢复

2. 导出过程观察
#

导出过程中可在另一窗口实时观察日志:

tail -f dds-fros_full_20260423.log

日志片段如下:

Last login: Thu Apr 23 20:30:33 2026 from 117.186.13.106
root@gkfx-sz01p-ecs-uat-his-ecs-dev:~# cd dds_bak/
root@gkfx-sz01p-ecs-uat-his-ecs-dev:~/dds_bak# tail -f dds-fros_full_20260423.log 
2026-04-23T20:43:20.832+0800    [###########.............]                 dms.Observation  121742/247927  (49.1%)
2026-04-23T20:43:20.832+0800
2026-04-23T20:43:22.785+0800    [########################]  dms-test.Observation  180802/180802  (100.0%)
2026-04-23T20:43:22.889+0800    done dumping dms-test.Observation (180802 documents)
2026-04-23T20:43:22.889+0800    writing ccm2.Indicator to archive 'dds-fros_full_20260423.gz'
2026-04-23T20:43:23.832+0800    [........................]       dms.QuestionnaireResponse    5560/247297   (2.2%)
2026-04-23T20:43:23.832+0800    [........................]  dms-test.QuestionnaireResponse    5560/181185   (3.1%)
2026-04-23T20:43:23.832+0800    [###########.............]                 dms.Observation  121742/247927  (49.1%)
2026-04-23T20:43:23.832+0800    [........................]                  ccm2.Indicator     101/126910   (0.1%)
2026-04-23T20:43:23.832+0800
2026-04-23T20:43:26.832+0800    [........................]       dms.QuestionnaireResponse    5560/247297   (2.2%)
2026-04-23T20:43:26.832+0800    [........................]  dms-test.QuestionnaireResponse    5560/181185   (3.1%)
2026-04-23T20:43:26.833+0800    [###########.............]                 dms.Observation  121742/247927  (49.1%)
2026-04-23T20:43:26.833+0800    [........................]                  ccm2.Indicator     101/126910   (0.1%)
2026-04-23T20:43:26.833+0800
2026-04-23T20:43:29.832+0800    [........................]       dms.QuestionnaireResponse    5560/247297   (2.2%)
2026-04-23T20:43:29.832+0800    [........................]  dms-test.QuestionnaireResponse    5560/181185   (3.1%)
2026-04-23T20:43:29.832+0800    [###########.............]                 dms.Observation  121742/247927  (49.1%)
2026-04-23T20:43:29.832+0800    [........................]                  ccm2.Indicator     101/126910   (0.1%)
2026-04-23T20:43:29.832+0800
^C
root@gkfx-sz01p-ecs-uat-his-ecs-dev:~/dds_bak# 

从导出日志可以确认以下事实:

  • 源端业务库中的多个集合被持续写入归档文件
  • 导出过程能够显示集合级进度与已完成对象
  • 日志中还出现了系统对象,如:
    • admin.system.users
    • admin.system.roles
    • admin.system.version

这说明本次导出归档中不仅包含业务库数据,也包含了部分系统元数据。

3. 导出结论
#

结合导出日志,可以得出以下结论:

  1. 本次 mongodump 导出已正常完成
  2. dump 归档中包含业务库数据及其相关元数据
  3. 归档中同时包含系统用户、角色等系统级对象,但这些内容不应在目标端直接恢复

五、目标端恢复:执行 mongorestore
#

1. 恢复策略
#

由于目标端不支持系统库迁移,同时目标端用户和角色需要手工创建,因此恢复时不能直接整包无选择恢复,而应采用业务库白名单恢复

本文恢复命令如下:

mongorestore --host 10.190.18.168  --port 8635  --ssl --tlsInsecure --sslCAFile ca-dds.crt --authenticationDatabase admin -u rwuser --password='******' --archive=dds-fros_full_20260423.gz --gzip --nsInclude='ccm.*' --nsInclude='ccm2.*' --nsInclude='dms.*' --nsInclude='dms-test.*' --nsInclude='form-builder.*' --nsInclude='freehealth.*' --nsInclude='fros.*' --nsInclude='fros-test.*' --nsInclude='gyyy.*' --nsInclude='stream.*' --nsInclude='test.*' >> restore_dds_fros_full_20260423.log 2>&1

这条命令的核心思路是:

  • 使用白名单方式,仅恢复业务库
  • 显式排除 adminconfiglocal 等系统库
  • 保证恢复对象范围与目标端约束一致

说明 文中的 mongosh 连接示例采用了 --tls 写法,而 mongorestore 示例中使用了 --ssl / --sslCAFile。在实际工具中这类参数通常存在兼容关系,但在正式实施文档中,建议统一工具参数风格,并明确说明证书校验策略。本文保留实操原始命令,以便完整还原执行过程。

2. 恢复过程观察
#

恢复过程中可通过以下命令实时查看日志:

tail -f restore_dds_fros_full_20260423.log

日志片段如下:

root@gkfx-sz01p-ecs-uat-his-ecs-dev:~/dds_bak# tail -f restore_dds_fros_full_20260423.log
2026-04-23T22:46:37.365+0800    index: &idx.IndexDocument{Options:primitive.M{"background":true, "name":"reqId_1", "ns":"form-builder._Idempotency", "sparse":true, "unique":true, "v":2}, Key:primitive.D{primitive.E{Key:"reqId", Value:1}}, PartialFilterExpression:primitive.D(nil)}
2026-04-23T22:46:37.365+0800    index: &idx.IndexDocument{Options:primitive.M{"background":true, "expireAfterSeconds":0, "name":"ttl", "ns":"form-builder._Idempotency", "sparse":true, "v":2}, Key:primitive.D{primitive.E{Key:"expire", Value:1}}, PartialFilterExpression:primitive.D(nil)}
2026-04-23T22:46:37.366+0800    no indexes to restore for collection gyyy.template
2026-04-23T22:46:37.366+0800    no indexes to restore for collection gyyy.test
2026-04-23T22:46:37.366+0800    restoring indexes for collection gyyy.ehr from metadata
2026-04-23T22:46:37.366+0800    index: &idx.IndexDocument{Options:primitive.M{"background":true, "name":"identifier", "ns":"gyyy.ehr", "unique":true, "v":2}, Key:primitive.D{primitive.E{Key:"identifier", Value:1}}, PartialFilterExpression:primitive.D(nil)}
2026-04-23T22:46:37.378+0800    no indexes to restore for collection gyyy.opt
2026-04-23T22:46:37.378+0800    no indexes to restore for collection gyyy.ethnic
2026-04-23T22:46:37.378+0800    no indexes to restore for collection fros-test.ehr
2026-04-23T22:46:41.113+0800    2431940 document(s) restored successfully. 0 document(s) failed to restore.
^C
root@gkfx-sz01p-ecs-uat-his-ecs-dev:~/dds_bak# 

从恢复日志中可以确认:

  • 11 个业务库均已纳入恢复范围
  • 恢复过程中未观察到失败记录
  • 日志明确显示多个集合正在从元数据恢复索引
  • 最终恢复统计为:
    • 2431940 document(s) restored successfully
    • 0 document(s) failed to restore

这说明本次恢复整体完成情况良好。

3. 关于系统用户与权限
#

这里需要明确区分两个层面:

源端归档中是否包含系统用户和角色
#

包含。

因为导出日志中已出现:

  • admin.system.users
  • admin.system.roles
  • admin.system.version

目标端是否恢复了系统用户和角色
#

没有恢复。

原因并非数据缺失,而是恢复策略有意规避了系统库:

  • 恢复命令仅对白名单业务库执行 --nsInclude
  • 恢复日志未显示 admin.system.usersadmin.system.roles 进入恢复流程
  • 目标端本身也不支持直接迁移系统库对象

因此,本次迁移中:

  • MongoDB 系统级用户/角色:源端归档中存在,但目标端不恢复
  • 业务库中的应用级集合,如 _User_RoleRoleDefinition 等,作为业务数据的一部分被正常恢复

六、迁移结果校验
#

恢复完成后,需要从多个维度验证迁移结果,而不仅仅依赖日志结论。

1. 校验数据库是否已恢复
#

连接目标端

root@gkfx-sz01p-ecs-uat-his-ecs-dev:~/dds_bak# mongosh "mongodb://rwuser:******@10.190.18.168:8635/admin?authSource=admin" --tls --tlsAllowInvalidHostnames --tlsCAFile /root/dds_bak/ca-dds.crt
Current Mongosh Log ID: 69ea17f42a1c583ab4f5c435
Connecting to:          mongodb://<credentials>@10.190.18.168:8635/admin?authSource=admin&directConnection=true&tls=true&tlsAllowInvalidHostnames=true&tlsCAFile=%2Froot%2Fdds_bak%2Fca-dds.crt&appName=mongosh+1.10.6
Using MongoDB:          4.2.0
Using Mongosh:          1.10.6
mongosh 2.8.2 is available for download: https://www.mongodb.com/try/download/shell

For mongosh info see: https://docs.mongodb.com/mongodb-shell/

[direct: mongos] admin> 

查看数据库列表:

[direct: mongos] admin> show dbs
admin              262 B
ccm                257 B
ccm2           44.71 MiB
config         38.58 KiB
dms           360.96 MiB
dms-test      154.04 MiB
form-builder       269 B
freehealth    287.84 KiB
fros           16.24 KiB
fros-test          257 B
gyyy           57.63 KiB
stream             260 B
test               267 B
[direct: mongos] admin> 

可以看到,目标端业务数据库均已出现。

说明 show dbs 中显示的库大小受底层存储、压缩、索引重建和碎片等因素影响。跨环境迁移后,数据库大小不完全一致并不必然表示数据异常。因此,容量只可作为参考指标,不能单独作为一致性结论依据。

2. 校验集合清单是否一致
#

ccm2 为例:

[direct: mongos] admin> use ccm2
switched to db ccm2
[direct: mongos] ccm2> show collections
_Idempotency
_Join:city:Province
_Join:disease:TagDefinition
_Join:diseaseManagement_:Patient
_Join:diseaseManagementDefinition_:Organization
_Join:district:City
_Join:planDefinition:DiseaseManagementDefinition
_Join:role:PractitionerRole
_Join:task:DiseaseManagement
_Role
_SCHEMA
_Session
_User
BasicInfoDefinition
City
CodeableConcept
Disease
DiseaseManagement
DiseaseManagementDefinition
DiseaseManagementIndicator
District
Doctor
Game
Generate_MongoLastResumeData
Generate_PatientObservation
Indicator
IndicatorDefinition
Location
Observation
Organization
Patient
PatientIndicator
PatientIndicatorLog
PlanDefinition
Practitioner
PractitionerRole
Province
Questionnaire
QuestionnaireResponse
RemoteStudy
Report
Reservation
RoleDefinition
Room
Schedule
Sms
Tag
TagDefinition
Task
Test
system.profile
[direct: mongos] ccm2> 

从集合清单看,目标端与源端保持一致。

3. 校验各业务库集合数量
#

在目标端再次执行集合统计脚本:

const conn = db.getMongo();

conn.getDBs().databases.forEach(({ name }) => {
  const curDb = conn.getDB(name);
  const collCount = curDb.getCollectionInfos(
    { type: "collection" },
    { nameOnly: true }
  ).length;

  print(`${name}: ${collCount}`);
});

目标端输出结果如下:

[direct: mongos] ccm2> use admin
switched to db admin
[direct: mongos] admin> const conn = db.getMongo();

[direct: mongos] admin> 

[direct: mongos] admin> conn.getDBs().databases.forEach(({ name }) => {
...   const curDb = conn.getDB(name);
...   const collCount = curDb.getCollectionInfos(
...     { type: "collection" },
...     { nameOnly: true }
...   ).length;
... 
...   print(`${name}: ${collCount}`);
... });const conn = db.getMongo();
admin: 5
ccm: 2
ccm2: 51
config: 14
dms: 53
dms-test: 53
form-builder: 7
freehealth: 3
fros: 11
fros-test: 2
gyyy: 6
stream: 3
test: 5

[direct: mongos] admin> 

与源端统计结果一致。

4. 建议补充的深度校验项
#

在正式生产切换前,建议进一步增加以下核验动作:

  • 对关键集合执行 countDocuments() 对比
  • 对关键集合索引执行 getIndexes() 对比
  • 抽样比对关键业务数据,例如按 _id 或业务主键检查若干记录
  • 对应用侧执行读写验证或关键接口冒烟测试

示例:

use ccm2
db.Patient.countDocuments()
db.Patient.getIndexes()
db.Patient.findOne()

这些校验项能够比“仅比较集合数量”提供更强的一致性证明。

七、推荐的标准实施步骤
#

综合本次实践,推荐将类似跨云迁移流程标准化为以下步骤:

步骤 1:确认版本与工具兼容性
#

  • 确认源端与目标端版本兼容
  • 确认 mongoshmongodumpmongorestore 版本与实例兼容
  • 确认目标端 TLS/SSL 连接要求

步骤 2:确认迁移窗口并停写
#

  • 选择业务低峰期
  • 停止源端业务写入
  • 固定切换窗口,避免导出期间继续产生增量

步骤 3:执行源端导出
#

  • 使用 mongodump --archive --gzip 导出
  • 实时观察导出日志
  • 记录源端数据库、集合数量与关键集合文档数

步骤 4:准备目标端账号与连接能力
#

  • 提前确认目标端账号权限
  • 准备 CA 证书等连接材料
  • 若目标端不支持系统库迁移,提前规划账号和角色创建方式

步骤 5:仅恢复业务库
#

  • 使用 --nsInclude 明确业务库白名单
  • 避免恢复 adminconfiglocal
  • 实时观察恢复日志并确认失败数为 0

步骤 6:执行迁移后核验
#

  • 核验数据库是否已出现
  • 核验集合数量是否一致
  • 核验关键集合文档数、索引、抽样数据是否一致
  • 验证业务应用连接与核心功能

步骤 7:切换与回退准备
#

  • 在目标端验证通过后进行应用切换
  • 如发现重大差异,保留源端作为回退入口
  • 在确认业务稳定前,不要立即销毁源端数据或归档文件

八、最终结论
#

结合源端导出日志、目标端恢复日志以及迁移后核验结果,本次 “华为云 DDS → 天翼云 DDS” 的迁移实践可以得出以下结论:

  1. 方案选择合理 基于 mongodump / mongorestore 的逻辑全量迁移,适用于本次存在停写窗口、希望降低迁移成本的跨云迁移场景。
  2. 源端导出成功 本次导出的归档文件已正常生成,且包含业务库数据及系统级元数据。
  3. 目标端恢复策略正确 通过 --nsInclude 精确恢复业务库,规避了系统库恢复风险,符合目标端约束要求。
  4. 业务库恢复完成 恢复日志显示全部业务库已纳入恢复范围,且恢复结果为 0 document(s) failed to restore
  5. 索引已随恢复流程重建 恢复日志中多次出现 restoring indexes for collection ... from metadata,说明目标端已执行索引恢复。
  6. 系统用户和角色未恢复到目标端 这是基于目标端限制和恢复策略刻意设计的结果,目标端系统账号与权限需单独创建。
  7. 迁移结果符合预期 从数据库列表、集合清单、集合数量和恢复日志来看,本次迁移整体成功;在正式切流前,仍建议结合关键集合文档数、索引和业务抽样进一步完成最终验收。