审校 | 重楼
最近许多组织都在寻求 DynamoDB 的替代方案。其主要原因除了最常提到的成本因素之外,节流、硬限制(hard limit)和供应商锁定等方面,也是迁移的各种动机。那么,当你需要将数据从DynamoDB 迁移到另一个数据库时,你是否会简单地从概念上认为,只需从一个数据源读取,转为写入另一个数据源即可呢?当你被要求保持迁移的一致性和安全性时,你是否会考虑到“双重写入(Dual-Write)”?鉴于一旦忽略了某个重要细节,就可能功亏一篑,你是否会想到选用工具,来协助解决此类问题?其中又有哪些典型的注意事项呢?
下面,我将从概述数据库迁移的原理出发,向你介绍与 DynamoDB 迁移相关的各项特定和重要特征,然后讨论用于与其他数据库集成、以及无缝迁移到其他数据库所采用的相关策略。
数据库迁移的工作原理
大多数数据库的迁移都遵循如下严格的步骤:
首先,捕获曾对源数据库做过的所有更改,以保证任何数据的delta修改都可以被后续予以重放(replay)。
其次,通过从源数据库读取并写入目标数据库的方式,进行数据复制。当然,你也可以先导出源数据库的备份,再简单地将其旁路加载(side-load)到目标数据库中。
至此,在初始数据被加载之后,目标数据库将会包含源数据库中的大部分记录。之所以称为“大部分”,是因为那些在此期间发生数据更改,将无法被加载进去。对此,下一步便是将源数据库生成的所有增量,重放到目标数据库中。一旦完成,这两个数据库便完全同步了。后续,你就可以开始切换应用了。
数据库迁移的工作原理
需要双重写入吗?
如果你熟悉 Cassandra 迁移,那么你可能听说过使用“双重写入”来完成迁移工作的建议。也就是说,你需要将代理源数据库中的每个写入器突变(writer mutation),以相同的记录写入目标数据库。
不过,并非每个数据库都实现了像 CQL 协议那样,允许写入器检索或操作记录时间戳的概念。这将阻止你在使用历史数据回填(back-filling)目标数据库时,对应用实施双重写入。毕竟,此举可能最终导致迁移的不一致,即:某些目标项可能无法反映其在源数据库中的最新状态。
那么,这是否意味着在 DynamoDB 迁移中使用双重写入属于错误之举呢?当然不是!考虑到你的 DynamoDB 数据表会让记录(TTL)每 24 小时过期一次。在这种情况下,使用简单地双重写入,并在 TTL 到期之后切换读取器的方式,去回填数据库的确没有意义。不过,如果你的 TTL 更长(比如一年),那么等待其过期显然不是移动数据的有效方法。
回填历史数据
虽然回填历史数据是大多数迁移中的强制步骤,但到底是否需要,则主要取决于你的用例。通常,你可以通过如下 3 种主要方式,回填 DynamoDB 中的历史数据:
ETL
为了实现ETL(提取-转换-加载),Apache Spark之类的工具会从扫描数据表开始,逐页读取结果,并使用结果来推断源表的架构(schema)。接着,它会用到 DynamoDB 数据表的生成读取器,而写入器会将检索到的数据摄入到目标数据库中。
这种方法非常适合执行那些简单的迁移,同时允许你在进行过程中转换(也就是ETL中的 T)数据。不过,此举容易出现如下问题:
- 架构推理:由于DynamoDB 表是无架构的,因此很难推断出其架构。所有表的属性(其中哈希键和排序键除外)可能不会显示在初始扫描的第一页上。此外,给定项的所有属性也可能不会投影到另一个项中。
- 成本:由于提取数据需要对 DynamoDB 数据表进行完全扫描,因此不可避免地会消耗 RCU(Read-Copy-Update)。如果 DynamoDB 的运能不足,就可能会对你的应用产生上游影响,因此它最终会片面地推高迁移的成本。
- 时间:迁移数据所需的时间往往与数据集的体量成正比。这意味着,如果你的迁移时间超过了 24 小时,则会超出 AWS 能够保证的事件可用性时段,因此也就可能无法在迁移后直接从 DynamoDB 数据流中进行重放。
数据表扫描
顾名思义,数据表的扫描只会在数据被加载到目标数据库之后,才从源 DynamoDB 表中检索所有的记录。与前面的 ETL 方法不同,这种方法的“提取”和“加载”部分是耦合的。数据会随着过程的推进而被写入,而且这里的每个步骤都是以分阶段的方式执行的。
好消息是该方法非常简单,你只需运行如下单个命令即可。一旦完成,你就得到了所有数据。
$ aws dynamodb scan --table-name source > output.json
然后,你将最终得到一个包含了源表中所有现有项的 JSON 文件。据此,你可以便捷地实施迭代并写入目标。除非你计划转换数据,否则你无需担心数据架构,毕竟你已事先知晓了所有键的属性。
注意,此方法仅适用于中小型数据表。与之前的 ETL 方法类似,它在扫描较大的数据表时,可能耗时较长,而且尚未包括你对其解析,以及后续将其加载到目的数据库所花费的时间。
S3 数据导出
如果你拥有大型数据集,或者是担心 RCU的使用会对实时流量产生影响的话,则可以将 DynamoDB 数据导出到 Amazon S3。此举可以让你轻松地转储数据表的全部内容,而不会影响 DynamoDB 表的性能。此外,如果你的回填过程的用时超过了 24 小时,那么你可以到后续再请求增量导出。
在实践中,若要请求将 DynamoDB 完整地导出至 S3,你只需运行如下命令:
$ aws dynamodb export-table-to-point-in-time --table-arn arn:aws:dynamodb:REGION:ACCOUNT:table/TABLE_NAME --s3-bucket BUCKET_NAME --s3-prefix PREFIX_NAME --export-format DYNAMODB_JSON
如果存在指定的 S3 存储桶的话,导出过程将在后台运行。你可以运行如下命令,来检查其完成情况。
$ aws dynamodb list-exports --table-arn arn:aws:dynamodb:REGION:ACCOUNT:table/source
{
"ExportSummaries": [
{
"ExportArn": "arn:aws:dynamodb:REGION:ACCOUNT:table/TABLE_NAME/export/01706834224965-34599c2a",
"ExportStatus": "COMPLETED",
"ExportType": "FULL_EXPORT"
}
]
}
该过程完成后,源表中的数据将会在之前指定的 S3 存储桶/前缀中可用。在里面,你将可以找到一个名为 AWSDynamoDB 的目录,其结构如下类似:
$ tree AWSDynamoDB/
AWSDynamoDB/
└── 01706834981181-a5d17203
├── _started
├── data
│ ├── 325ukhrlsi7a3lva2hsjsl2bky.json.gz
│ ├── 4i4ri4vq2u2vzcwnvdks4ze6ti.json.gz
│ ├── aeqr5obfpay27eyb2fnwjayjr4.json.gz
│ ├── d7bjx4nl4mywjdldiiqanmh3va.json.gz
│ ├── dlxgixwzwi6qdmogrxvztxzfiy.json.gz
│ ├── fuukigkeyi6argd27j25mieigm.json.gz
│ ├── ja6tteiw3qy7vew4xa2mi6goqa.json.gz
│ ├── jirrxupyje47nldxw7da52gnva.json.gz
│ ├── jpsxsqb5tyynlehyo6bvqvpfki.json.gz
│ ├── mvc3siwzxa7b3jmkxzrif6ohwu.json.gz
│ ├── mzpb4kukfa5xfjvl2lselzf4e4.json.gz
│ ├── qs4ria6s5m5x3mhv7xraecfydy.json.gz
│ ├── u4uno3q3ly3mpmszbnwtzbpaqu.json.gz
│ ├── uv5hh5bl4465lbqii2rvygwnq4.json.gz
│ ├── vocd5hpbvmzmhhxz446dqsgvja.json.gz
│ └── ysowqicdbyzr5mzys7myma3eu4.json.gz
├── manifest-files.json
├── manifest-files.md5
├── manifest-summary.json
└── manifest-summary.md5
2 directories, 21 files
那么,我们又该如何从这些文件中恢复呢?在此,你需要使用 DynamoDB 低级 API。值得庆幸的是,你无需深入研究其详细信息,因为作为一种入门的方式,AWS 已提供了 LoadS3toDynamoDB的示例代码。你只需使用目标数据库的写入器逻辑,去覆盖 DynamoDB 连接即可。
流式处理 DynamoDB 更改
无论你是否需要回填数据,可能都希望从 DynamoDB 处捕获事件,以确保两者彼此同步。对此,DynamoDB 数据流可被用于捕获在源 DynamoDB 表中执行的任何更改。
DynamoDB Streams Kinesis Adapter
AWS 提供了 DynamoDB Streams Kinesis Adapter,以便你通过 Amazon Kinesis 客户端库,比如 Apache Spark 中的 kinesis-asl 模块,处理来自 DynamoDB Streams 的各种事件。除了历史数据的迁移,你只需将事件从 DynamoDB 数据流传输到目标数据库,便可实现两个数据存储的同步。
尽管这种方法可能会引入陡峭的学习曲线,但作为迄今为止最灵活的方法,它甚至允许你使用 AWS 生态系统的外部事件。而这对于想要切换到不同的提供商尤为重要。对此,AWS 提供了有关如何使用源 DynamoDB 表到目标表中的事件的一套演示流程。
AWS Lambda
如你所知,Lambda 函数既易于上手,又可自行处理所有检查点的逻辑,还能与 AWS 生态系统无缝集成。使用该方法,你只需将应用逻辑封装在 Lambda 函数中即可。这可以让你将事件写入目标数据库,而无需处理诸如检查点或流中的分片数等 Kinesis API 逻辑。
通过该方法,你可以将捕获到的事件直接加载到目标数据库中。而如果存在 24 小时保留限制的问题,你也可以便捷地在Amazon SQS等其他服务中,流式传输和保留这些记录,以便后续进行重放。有关如何使用 Lambda 函数的示例,请参阅 AWS 文档。
小结
上文和你深入探讨了 DynamoDB 迁移的工作原理,以及它与其他数据库的区别。我们也讨论了回填历史数据,并将数据流更改传输到另一个数据库的不同方法。最后,我们还利用你可能熟悉的 AWS 工具探讨了端到端迁移。综上所述,鉴于我们有着多种不同的方法来完成迁移,而且每一种都会存在一系列的优缺点,因此在开始数据库迁移之前,我们需要仔细规划,综合比较迁移所需的所有工具和策略,并对过程中涉及的各个步骤有着透彻的理解。
译者介绍
陈峻(Julian Chen),51CTO社区编辑,具有十多年的IT项目实施经验,善于对内外部资源与风险实施管控,专注传播网络与信息安全知识与经验。
原文DynamoDB: How To Move Out,作者:Pratik Patel