亚马逊AWS官方博客

大型 MongoDB 数据库迁移到 DocumentDB Elastic Cluster 的最佳实践

1.背景

目前,文档型数据库由于灵活的 schema 和接近关系型数据库的访问特点,被广泛应用,尤其是游戏、互联网金融等行业的客户使用 MongoDB 构建了大量应用程序,比如游戏客户用来处理玩家的属性信息;又如股票 APP 用来存储与时间线相关的行情数据。随着时间的推移和业务的发展,MongoDB 库越来越大,大库治理是必须面临的问题。

一般来讲,大库治理有如下几种方案。一是做冷热数据隔离,将数据根据使用频率分为热、温、冷、冻级别,超过一定时间的冷数据,转储到另一个冷库或低成本存储的数据库;热库只保留近期访问频繁的数据;二是做垂直拆分,比如大系统有多个集合,按照模块进行垂直划分,把不同模块对应的集合拆分到不同库,实现数据量和访问量的垂直分离;三是做水平拆分,比如选择 userid 的哈希值,将大的集合水平拆分到多个库,实现整体存储和计算能力的扩展。第四,也有部分业务,它的历史数据的使命完成,走完生命周期,可以直接删除。这 4 种方案,各有利弊,且需要根据实际业务场景进行选型。而很多场景下,客户会选择水平 sharding,主要原因如下:

  • 很多业务需要经常查询历史数据, 水平 sharding 不需要删除或分离历史数据;
  • 长远来看,水平 sharding 的扩展性更好,可以支撑更大的业务规模。

DocumentDB Elastic Cluster 是 AWS 提供的一个很好的支持水平 sharding 的云数据库服务。本文,主要针对客户从 MongoDB 副本集架构迁移到 DocumentDB Elastic Cluster 的过程中,如何进行海量数据迁移的问题,进行研究,并提供最佳实践。

2.可选迁移方案

众所周知,含有大数据量的数据库的迁移,是比较有挑战性的问题。数据库在不断的读写,不仅需要在目标库完成当前全量数据的初始化,也需要把初始化期间的数据变化同步到新库。以下是迁移方案示意图:

我们知道 MongoDB 记录文档变化的方式有两种:oplog 和 change stream。由于,oplog 或 change stream 的存储空间是有限的,因此全量初始化阶段的迁移速度是必须要考虑的因素。另外,增量同步阶段的速度也必须大于源数据库的变化速度,这样才能实现新旧数据库的数据一致。这两个阶段,我们都需要依赖稳定、高效的工具来完成。尤其在大型数据库的迁移时,甚至要配合一定的数据迁移策略(比如并行、压缩;冷、热数据分别迁移;不同集合分别迁移等)。

我们有 3 种可行的迁移方案。

  • AWS DMS 全量 + 增量迁移;
  • Mongoshake 全量 + 增量迁移;
  • Mongodump/mongorestore + DMS 增量迁移

方案 1:AWS DMS 全量 + 增量

DMS 是 AWS 的一项云服务,允许迁移关系数据库、MongoDB 数据库和其他类型的数据存储。我们可以使用 DMS 执行一次性迁移,或复制源库正在进行的更改以保持源和目标同步。DMS 在全量迁移阶段提供了 Auto segmentation 和 Range segmentation 的方式来并行加速迁移;在 CDC 增量阶段,3.5 beta 版也支持并发方式写入 DocumentDB。

配置参考:https://docs.aws.amazon.com/zh_cn/dms/latest/userguide/CHAP_Tasks.CustomizingTasks.TaskSettings.TargetMetadata.html

方案 2:Mongoshake 全量 + 增量

开源的 Mongoshake,也支持迁移写入 DocumentDB。由于它属于开源产品,优势是社区活跃,遇到问题可以定制开发解决,迁移速度较快;劣势是遇到问题可以获得的技术支持力度较低,用户需要自己定位或求助社区。

方案 3:Mongodump/mongorestore + DMS 增量

mongodump 是 MongoDB 官方提供的备份工具,它可以从 MongoDB 数据库读取数据,并生成 BSON 文件,然后通过 mongorestore 工具恢复到 MongoDB。它也同样支持从 DocuemntDB 备份数据。而 mongodb-database-tools 的 6.1 版本也支持恢复到 DocumentDB Elastic Cluster。这种方案的优势是稳定快速,缺点是增量同步能力不足。但是,可以借助 DMS 的增量同步能力。重点是需要选择好增量同步的起始位点,防止数据丢失。

以上三种方案,各有优缺点,如下表。

方案编号 迁移工具与方案 优势 劣势
方案1 AWS DMS 全量 + 增量 全托管服务,配置简单,可观测性好。 Full load 速度稍慢;
不支持索引迁移。
方案2 Mongoshake 全量 + 增量 开源工具,可以定制开发;
全量迁移速度较快。
源端不支持 DocumentDB,增量阶段不支持批量写入 DocumentDB;
不支持索引迁移
方案3 Mongodump/mongorestore + DMS 增量 MongoDB 原生备份恢复工具,恢复速度快;
支持索引迁移。
导出、传输、导入三个阶段分别操作,自动化欠佳;借助 DMS 实现增量同步。

使用 DMS 托管服务,用户配置迁移任务最方便,整个迁移过程,日志清晰、速度直观,可观测性较好。Mongoshake 在增量写入 DocumentDB 环节速度略慢,在 TPS 较高的场景不适用;而 mongodump 和 mongorestore 在 MongoDB 大数据库迁移场景上,速度比 DMS full load 更快。大库迁移是否成功的一个非常重要因素是迁移速度。因此,我建议选择方案 3。下文重点介绍 Mongodump/mongorestore + DMS  增量方案的详细步骤(其他方案的步骤及相关迁移性能调优方法,后续其他文章补充)。

3. Mongodump/mongorestore + DMS 增量方案详细步骤

  • 环境说明
迁移方案 源库配置 目标库配置 迁移工具配置
Mongodump/mongorestore + DMS 增量 MongoDB4.2 , 8 vCPUs, 64G DocumentDB Elasti Cluster,
3 shards, 16 vCPUs per shard
c5a.8xlarge(for mongodump/mongorestore);
dms.r5.4xlarge(DMS CDC)

3.1 EC2 环境部署

EC2 主要用于部署 mongo tools 和 DocumentDB-tools,以及存放从源库导出的 Bson 文件。mongodump 会把数据导出为 BSON 文件,大表的 BSON 文件也会占用很大空间,因此,需要配置较大的数据盘。本案例中,配置了 3TB 的数据盘。

(1)选择合适的操作系统

操作系统选择 Amazon linux 2 即可,如下图

(2)选择合适的机型

由于 mongorestore 支持并发导入,用户可以定制并行数,建议机型的 vCPU 数量与并行数相同或 2 倍。本文拟采用 16 并发导入测试,因此选择机型 m5a.4xlarge。

(3)选择与 DocDB 相同的 AZ、VPC 和 Subnet

避免导入时出现跨可用区而提高延迟,建议 mongodb tools 的 EC2 与 DocDB Elastic Cluster 部署在同一个 AZ、VPC,通过编辑网络设置,选择不同的 Subnet 可以选择合适的 AZ。

(4)增加一块合适尺寸的磁盘

根据 mongodump 导出的 BSON 文件大小,选择一块合适尺寸的 EBS 盘。例如下图,我们本次测试的数据库大概 2.3TB,因此选择 3000GiB 的 EBS 盘。然后点击 Launch instance 部署 EC2 实例。

(5)创建存放 dump 文件的文件系统并挂载目录

第(4)步只是创建了额外数据盘,当 EC2 部署好后,还需要创建文件系统,并创建一个实际存储 dump 文件的目录,挂载到文件系统。具体参考:https://docs.aws.amazon.com/zh_cn/AWSEC2/latest/UserGuide/add-instance-store-volumes.html#making-instance-stores-available-on-your-instances。如果是已创建好的 EC2,也可以添加 EBS 设备作为数据盘,具体添加方式参考:https://docs.aws.amazon.com/zh_cn/AWSEC2/latest/UserGuide/ebs-attaching-volume.html

3.2 mongo tools 安装

说明:本案例添加了一块数据盘,创建了目录/backup 存放 dump 文件。如果源 mongoDB 不在云内,则 3.2 和 3.3 两个步骤在源 mongoDB 所在 IDC 执行,然后把 dump 文件传输到 3.1 步骤部署的 EC2。以下的步骤以客户在 AWS 云内自建 MongoDB 为例。

(1)登陆 EC2 下载工具

# 进入工具安装目录,自定义一个即可
cd /data/mongo
wget https://fastdl.mongodb.org/tools/db/mongodb-database-tools-amazon2-x86_64-100.6.1.tgz

注意:mongo tools 的版本必须是 mongodb-database-tools-amazon2-x86_64-100.6.1,其他版本的 mongoresotre 不支持导入 DocumentDB Elasti Cluster 集群。

(2)解压安装

tar zxvf mongodb-database-tools-amazon2-x86_64-100.6.1.tgz

3.3 导出源库数据

执行 mongodump 导出数据; -d 指定 database, -c 指定集合名称, -o 指定 dump 输出目录。详细的参数,可以通过 mongodump –help 查看。

cd /data/mongo/mongodb-database-tools-amazon2-x86_64-100.6.1/bin
nohup ./mongodump -h ip-172-xxxx.ec2.internal:27017 -u <YourUser> -p <YourPassword> -d demodb -c usertable -o /backup > dump.out 2>&1 &

# 查看导出进度
tail -f ycsb_test2.out

2023-04-12T04:27:01 writing demodb.usertable to /backup/demodb/usertable.bson
2023-04-12T08:48:57.031+0000    done dumping demodb.usertable (2000000000 documents)
# 查看备份文件
ls -l /backup/*
-rw-rw-r-- 1 ec2-user ec2-user 2335759066463 Apr 12 08:48 usertable.bson
-rw-rw-r-- 1 ec2-user ec2-user           204 Apr 12 04:27 usertable.metadata.json

当看到 done dumping xxxxdb.xxxxtable 这个信息,表示导出完成。

注意:需要记录开始导出的 UTC 时间 2023-04-12T04:27:01,配置 DMS 的增量任务时会基于该事件戳设置起始时间。

3.4 在目标 DocDB 创建启用 sharding 的数据库和集合

由于我们要把一个非 sharding 的集合写入到 sharding 集合,因此需要提前设计好 sharding-key,并在 DocumentDB Elastic Cluster 创建好 sharding 集合,否则 mongorestore 会按照 one-shard 的模式导入,达不到水平 sharding 的效果。

(1)创建目标库

目标库需要启用 enablesharding。创建库名为 tempdb 的库,且启用 sharding 的语法:

db.runCommand( { enablesharding :"tempdb"});
{ "ok" : 1 }

(2)创建集合

sharding-key 选择默认的_id,按照 hash 方式分片。目前 Elastic Cluster 只支持 hash 模式。创建语法如下:

sh.shardCollection( "demodb.usertable", { "_id": "hashed" } )
{ "ok" : 1 }

3.5 导入数据到目标库

注意:如果源 mongoDB 在客户的 IDC,或与 DocDB 不在一个 AZ,需要提前传输到目标 DocDB 所在的 EC2(即 3.1 步骤部署的 EC2 实例)。以下是使用 mongorestore 导入数据的命令:

nohup ./mongorestore -hdocdb-cluster1-xxxx.us-east-1.docdb-elastic.amazonaws.com \
--ssl -u <YourUser> -p <YourPassword> -c usertable -d tempdb \
--dir=/backup/demodb/usertable.bson \
--numInsertionWorkersPerCollection=16 > mongorestore_log.out 2>&1 &

参数说明:–numInsertionWorkersPerCollection 可以指定并发导入的 worker 数,与 DocDB 的 shard 数量没有直接关系;-d 可以指定目标数据库名称,可以与源库不同。–dir 指定 BSON 文件的绝对地址。

2023-04-12T10:23:53.924+0000    checking for collection data in /backup/demodb/usertable.bson
2023-04-12T10:23:53.924+0000    reading metadata for tempdb.usertable from /backup/demodb/usertable.metadata.json
2023-04-12T10:23:53.982+0000    restoring to existing collection tempdb.usertable without dropping
2023-04-12T10:23:53.982+0000    restoring tempdb.usertable from /backup/demodb/usertable.bson
2023-04-12T10:23:56.921+0000    [........................]  tempdb.usertable  141MB/2175GB  (0.0%)
2023-04-12T10:23:59.920+0000    [........................]  tempdb.usertable  322MB/2175GB  (0.0%)
2023-04-12T10:24:02.921+0000    [........................]  tempdb.usertable  555MB/2175GB  (0.0%)
2023-04-12T10:24:05.920+0000    [........................]  tempdb.usertable  772MB/2175GB  (0.0%)
2023-04-12T10:24:08.921+0000    [........................]  tempdb.usertable  1018MB/2175GB  (0.0%)
2023-04-12T10:24:11.921+0000    [........................]  tempdb.usertable  1.21GB/2175GB  (0.1%)
2023-04-13T07:04:32.920+0000    [#######################.]  tempdb.usertable  2175GB/2175GB  (100.0%)
2023-04-13T07:04:35.920+0000    [#######################.]  tempdb.usertable  2175GB/2175GB  (100.0%)
2023-04-13T07:04:38.117+0000    [########################]  tempdb.usertable  2175GB/2175GB  (100.0%)
2023-04-13T07:04:38.117+0000    finished restoring tempdb.usertable (2000000000 documents, 0 failures)
2023-04-13T07:04:38.117+0000    restoring indexes for collection tempdb.usertable from metadata
 2023-04-13T07:05:43.131+0000    index: &idx.IndexDocument{Options:primitive.M{"name":"idx_name", "ns":"tempdb.usertable", "v":2}, Key:primitive.D{primitive.E{Key:"name", Value:1}}, PartialFilterExpression:primitive.D(nil)}
2023-04-13T07:05:43.132+0000    2000000000 document(s) restored successfully. 0 document(s) failed to restore.

当看到 document(s) restored successfully 的信息,表示导入完成。

迁移索引

默认选项下,在迁移数据后,Mongorestore 会自动迁移索引。用户也可以选择在 mongorestore 时不迁移索引(在 mongorestore 后增加选项–noIndexRestore),然后使用 Amazon DocumentDB 索引工具从源 Amazon DocumentDB 集群导出索引并恢复到目标库。具体可参考 AWS 开发人员指南:https://docs.aws.amazon.com/zh_cn/documentdb/latest/developerguide/docdb-migration.versions.html#docdb-migration.versions-step3

对于索引,目前需要注意以下几种索引,在 Elastic Cluster 不支持:

  • Sparse indexes
  • TTL indexes
  • Geospatial indexes
  • Background index create

3.6 监控写入指标

可以登陆 CloudWatch 查看,每个 shard 的导入速度。通过 CloudWatch→All metrics→ DocDB Elastic→ Search “DocumentsInserted” ,勾选每个 shard,生成 Dashboard,如下图所示:

可以看到本案例有 3 个 shard,每个 shard 的数据写入大概是 22万行/秒。

3.7 增量同步

通过 DMS console 配置增量同步任务,具体参考如下:

(1)创建 endpoint

和其他迁移任务一样,需要分别创建 source endpoint 和 target endpoint;其中,source endpoint 是 MongoDB 的 endpoint,自建 MongoDB 则选择手动输入连接信息的方式,如下图,需要注意紫色部分的选择。

target endpoint 是 DocumentDB 的 endpoint,大致与上图一致。

(2)创建复制实例

CDC 的速度与复制实例的机型相关,您可以选择不同机型进行测试。由于实例只是用于迁移工作,并非长期同步,建议选择单可用区的实例(完成迁移后释放),这样方便把复制实例与 DocumentDB 部署在同一个可用区,提高 CDC 写入速度。另外,目前要使用 DMS 的并行 CDC 写入 DocumentDB 功能,需要选择 3.5.0(beta)及以上版本。

(3)创建增量同步任务

创建任务时,输入 identifier,选择上文配置的复制实例和 endpoint;然后在 Migration Type 选择“Replicate data changes only”表示只做 CDC 增量同步。

接下来的任务设置,可以启用自定义启动模式,即声明从哪个时间戳开始抓取 change stream 并开始写入目标 DocumentDB。请输入正确的开始时间,这个时间必须是开始 Mongodump 的时间点,才能保证数据一致性,它在上文已经被保存。

如上图,Task settings 中,Target table preparation mode 需要设置为 Do nothing,即告知 DMS 如果发现目标库已存在集合选择忽略。原因是我们创建了启用 sharding 的集合,如果 DMS 做表的重建,它只能重建 1 个普通的非 sharding 的集合,这与我们的分片设计初衷背离。为了更好的监控任务,我们建议勾选 Turn on CloudWatch logs 的选型。

然后配置集合的 Mapping rule,声明同步的数据库名字和集合名字。如果您是全库的表在的一个任务做增量,则不需要单独配置 table name。如果 schema 的 name 在源库和目标库不同,也可以在这里配置 mapping 规则。

在创建页面的下方,关于任务创建后是否立即启动迁移任务的选项,由于我们要修改并行 CDC 的参数,所以这里需要选择稍后手动启动,勾选“Manually later”。最后点击“Create Task”完成任务创建。

(4)修改并行 CDC 参数

任务创建后,任务应该是 Ready 状态(未启动)。现在我们开启 DMS CDC 针对 DocumentDB 的 paralley apply 功能。勾选 task,然后点击 Modify,找到 Task settings,选择 Json editor(如下图)。

# 修改 TargetMetadata 中以下 3 个参数(以下参数值是配置示例)
"ParallelApplyBufferSize": 1000,
"ParallelApplyQueuesPerThread": 200,
"ParallelApplyThreads": 16,

然后,点击保存。

这三个参数非常重要,从 DMS 3.5(beta)引入,目的是提高 CDC 写入 DocumentDB 的速度,提高大容量文档数据库的迁移成功率,参数解释:

  • ParallelApplyThreads:指定在 CDC 加载期间 AWS DMS 用于将数据记录推送到目标库的并发线程数。
  • ParallelApplyBufferSize:指定在 CDC 加载期间在每个缓冲区队列中存储的最大记录数。
  • ParallelApplyQueuesPerThread:指定在 CDC 期间每个线程访问的队列数量。

具体参考:https://docs.aws.amazon.com/zh_cn/dms/latest/userguide/CHAP_Tasks.CustomizingTasks.TaskSettings.TargetMetadata.htmlj

(5)修改 CDC 开始时间

上文提到,DMS 做 CDC 任务,需要在全量数据初始化之后。如果在(3)步没有选择正确的时间,这一步骤你可以通过 AWS CLI 重新设置。为了数据一致性,需要让 DMS 把开始数据初始化的时间点作为起始 checkpoint 拉取 ChangeStream。

前提:配置好 AWS CLI

Step1. 可以在源库确认 oplog 的范围

rs0:PRIMARY> rs.printReplicationInfo()
configured oplog size:   8961.375MB
log length start to end: 304secs (0.08hrs)
oplog first event time:  Wed Apr 12 2023 10:06:24 GMT+0000 (UTC)
oplog last event time:   Wed Apr 12 2023 10:11:28 GMT+0000 (UTC)
now:                     Wed Apr 12 2023 10:11:30 GMT+0000 (UTC)

注意:Step2 中的—cdc-start-time 指定的值必须在以上范围内,否则任务启动时会失败,原因是找不到 oplog。虽然 DMS 默认选择 change stream 进行 CDC 迁移,但 change stream 的恢复 token 也与 oplog 的时间戳有直接关联。

Step2. 执行修改

aws dms modify-replication-task \
--replication-task-arn "arn:aws:dms:us-east-1:7301234567:task:xoxxxo" \
--cdc-start-time "2023-04-12T10:06:50"

注意:DMS task 必须在 stopped 状态才能执行以上命令修改。

–replication-task-arn 是 DMS migration task 的 ARN。

–cdc-start-time 是启动 mongodump 时的开始时间,以上启动导出时有记录,此处的具体时间值仅作参考。

(6)启动 CDC 任务

启动任务后正式开始从 MongoDB 到 DocumentDB 的 CDC 数据同步。具体方法:勾选需要启动的 CDC 任务,点击“Actions”,选择 Restart/Resume,即可启动任务。

注意:如果 CDC 任务曾经暂停失败过,而进行了 cdc-start-time 的修改,则启动任务后,要再做一次选择 Restart or Resume,请选择 Restart。如果误选 Resume,则 cdc 会从上次暂停或任务失败的时间点开始拉去 change stream,导致手动设置的 cdc-start-time 不生效。

(7)监控 CDC 任务

任务启动后,如果你需要了解增量同步的状态,可以通过 DMS 的“CloudWatch metrics” 或 “View CloudWatch Logs”进行监控。

  • CloudWatch metrics

如下图,进入任务后,点击 CloudWatch metrics 的选项卡,选择 CDC 任务,可以看到 CDC latency source 和 CDC latency target 的指标,如果看到延迟不缩小,则可能存在数据同步效率问题。如果延迟在逐渐缩小,则说明增量在逐步追齐。

  • View CloudWatch Logs

通过 CloudWatch Logs 可以看到具体 DMS 执行的日志。比如可以通过 Log 确认 Parallel Apply 是否生效,以及目标库 DocumentDB 是否在正常应用日志变化。

看到上图紫底色和紫色框“[TARGET_APPLY]I: Working in bulk apply mode”表示 parallel apply 启用成功。下图所示为正常的写入日志,表示每次从队列中取出 200 个记录应用到目标 DocumentDB 库。

4.总结

本文主要从客户在使用 MongoDB 支撑海量文档型数据时,遇到的扩展难问题入手,提出了进一步解决扩展性的方案;并针对 MongoDB 副本集大库迁移难点,提供了迁移到水平 sharding 的高伸缩性 DocumentDB Elastic Cluster 的三种方案。然后,详细介绍了 mongodump/mongorestore 全量 + DMS 增量的迁移方案详细步骤。这种迁移方案非常高效,是 MongoDB 迁移 AWS 的有效方案,也支持 DocumentDB instance-based cluster 向 Elastic Cluster 的迁移。值得注意的是任何数据库都不会保留无限制的 change stream 和 oplog,一旦在全量迁移期间产生增量日志被删除的情景,则迁移过程就不能保证数据最终一致性,进而导致整体迁移无效。比如 DocumentDB Instance-based cluster 的 change stream 支持的最大保存周期为 7 天(建议按照您的实际环境测试全量迁移速度,估算 change stream 保留期间,可最大迁移的数据量)。因此,如果您发现某个业务的 MongoDB 或 DocumentDB 的存储容量达到了 TB 级别,且数据量还有增加的趋势,请尽早进行 sharding 的设计和改造。早发现早优化,防止数据库过于庞大,使架构优化和数据迁移越来越麻烦。

5.参考文档

本篇作者

金川

AWS 数据库解决方案架构师,负责基于 AWS 的数据库的解决方案咨询与架构设计。在加入 AWS 之前曾在华为、阿里云等公司工作多年,在数据库选型与架构设计、数据库优化、数据迁移、大数据和数仓建设方面有丰富的技术经验,在金融、互联网、通信等行业有丰富的设计和实施经验。