在过去的一年里,RevenueCat 团队一直在构建一个新的数据平台来支持数据驱动的功能,比如 Lists(它可以让你对你的客户进行分组和分析)和新版本的 Charts(它可以帮助你分析和可视化你的应用业务)。在 RevenueCat,我们的数据源自 PostgreSQL 事务数据库。但是,我们的一些功能(如列表和图表)由 Redshift 实例提供支持,我们将其用于数据分析。就上下文而言,我们的 PostgreSQL 数据库每秒处理 800 多个更改。每天超过 7000 万行。所以找到一个可靠的复制机制对我们来说至关重要。在这篇博文中,我们将讨论使用 AWS DMS 将 PostgreSQL 复制到 Redshift 的利弊。我们希望帮助其他开发人员和组织做出这一重要决定,并为您在生产环境中使用 DMS 时可能遇到的潜在问题提供一些指导。我们的团队决定使用 AWS DMS 将我们的 PostgreSQL 生产数据库复制到 Redshift。 DMS 让您只需几个步骤即可设置复制方案。我们不会在这篇文章中讨论如何实际设置 DMS,因为根据源/目标是本地、IaaS 还是 PaaS,有太多不同的配置。在我们进入如何配置 DMS 之前,了解一些术语很重要。 DMS 源是您要从中复制数据的系统。它可能是关系数据库(例如 PostgreSQL 或 MySQL)、文档数据库(例如 Amazon DocumentDB),甚至是 S3 存储桶。 DMS 目标是复制过程的目标。就像源一样,目标可以是关系或文档数据库或 S3 存储桶。您可以在 DMS 文档中找到有关支持的源和目标的更多信息。以下是复制过程架构的一般概述(请注意,DMS 复制实例在 EC2 实例中运行):
AWS DMS 有很多很棒的文档,因此如果您考虑将 DMS 用于自己的目的,我们建议您阅读关键组件。但是,文档中有些工作流程并不清楚,所以我们想花点时间澄清一下。 DMS 任务从 PostgreSQL 复制槽读取更改(上图中的“源捕获”步骤)。在某些时候,基于 BatchApplyTimeoutMin、BatchApplyTimeoutMax 和 BatchApplyMemoryLimit 设置,一批更改应用于目标(上图中的“目标应用”步骤)。应用所有更改后,该过程会从第 1 步重新开始。那么为什么这很重要?好吧,除了有助于了解 DMS 的工作原理之外,我们还想强调批量更新部分。 DMS 将始终尝试在批量模式下应用更改,这意味着使用 INSERT SELECT 语句执行 INSERT 语句,使用 DELETE FROM table WHERE ID IN (SELECT ID FROM middle_table) 语句执行 DELETE 语句,使用从中间表中更新 mytable WHERE mytable.ID =中间表.ID。在某些情况下,DMS 会切换到一对一模式,这对目标复制延迟有很大影响。我们稍后将解释如何解决此问题。安全。对我们而言,最大的因素之一是我们将 AWS 用于我们的所有系统,而 DMS 使我们能够避免让我们的数据离开 VPC。我们不希望任何第三方在我们的 VPC 之外处理或存储我们的数据。使用方便。您只需点击几下即可设置 DMS。而且 AWS UI 非常简单——您可以使用向导或直接在 JSON 中设置 DMS(这有利于对配置进行版本控制)。
费用。在内部开发像 DMS 这样的工具会花费很多。 DMS 的另一个好处是他们不会为产品本身向您收费——您只需为运行复制任务的 EC2 实例付费。很好的文档。有大量关于配置 DMS、最佳实践等的文章。自动 DDL 传播。这可能是 DMS 最强大的功能之一。它会自动将源中的 DDL 更改传播到目标数据库。这意味着开发人员不必担心迁移复制目标模式。 (这就是说服我们尝试 DMS 的原因!)它在 99% 的时间内都有效。正如我们稍后将看到的,在某些情况下 DMS 有一些限制,但总体而言,它运行得非常好。 Cloudwatch 指标。如果您要使用 DMS,则需要设置警报,尤其是针对目标延迟。我们强烈建议花一些时间配置好的警报——从长远来看,它会为您节省大量时间和麻烦。复杂的产品。 DMS 是一个通用的复制软件——它支持很多源和很多目标,每个源和目标都有自己特定的配置细节。同时,他们之间也有很多相似之处。很难判断哪些配置设置适用于哪些源和目标。缺少更改。我们不知道为什么,但有时复制过程会跳过一行。它似乎发生在具有高容量和每秒更改次数的表中。另一件需要注意的事情:有时当复制实例重新启动时,更改可能会丢失。
复制实例内存泄漏。复制任务在 EC2 实例之上运行。实例的大小无关紧要,但任务会从实例中消耗内存,直到任务失败并需要重新启动。一对一模式切换。我们认为这肯定有一些理由,但是每次批量 UPDATE 语句影响的行数比预期的少,因为缺少行,它会切换到一对一模式,逐一应用所有 UPDATE,然后切换回来到批量模式。这个问题(以及缺失的更改)会严重影响您的生产环境,因为它会使目标延迟飙升。因此,您最好为目标延迟指标设置一些 Cloudwatch 警报,否则如果您的源数据库每秒或每分钟复制许多更改,您可能会遇到麻烦。任务状态没有反映正在发生的事情。我们看到了一些任务实际上失败的场景,但状态没有更新为失败。布尔值映射到 varchar。在 Redshift 中,您不能直接将 varchar 转换为 boolean。由于 DMS 使用一个中间表,其中所有列都是 varchar,布尔值从 PostgreSQL 转换为 Redshift 作为 varchar(14)。我们试图强制 DMS 使用 boolean 作为目标,但它失败了,这使得任务切换到一对一模式。简而言之,布尔值是字符串,这对我们来说不是最方便的。深入研究我们的特定配置没有多大意义,因为我们几乎遵循了 AWS DMS 文档。如果您正在考虑使用 DMS,我们强烈建议您阅读源系统和目标系统的具体注意事项。出于我们的目的,我们遵循有关使用 PostgreSQL 作为源并使用 Redshift 作为目标的文档。相反,我们认为分享我们在从 PostgreSQL 复制到 Redshift 时可能对您有用的文档中没有的特定细节会很有用。如缺点部分所述,当批量 UPDATE 应用于比预期更少的行时,DMS 任务切换到一对一模式。 DMS 通过减去与该操作相关的索引来确定有多少行将受到 UPDATE 语句的影响。例如,假设这是 UPDATE 查询:
在本例中,如果 UPDATE 影响的行少于 1000 行,DMS 将切换到一对一模式。在 RevenueCat,我们创建了一个辅助项目来处理我们在此过程中发现的所有 DMS 问题。出于这个特定目的,我们创建了一个守护进程(复制检查器),它每三分钟运行一次,并检查集群中与 DMS 相关的 UPDATE 数量(这很容易做到,因为 DMS 使用 DMS 版本向 UPDATE 语句添加了注释)。当 DMS 切换到一对一模式时,复制检查器会启动并通过执行以下操作来修复它: 当集群中的 UPDATE 数量大于阈值时(这可以通过检查历史数量来动态计算) UPDATE 和 UPDATE 的增量与最后 X 分钟相比,但我们最终设置了一个固定值以防止误报),复制修复程序开始修复过程。修复程序将查找已回滚的最新 UPDATE(这是通过查询 Redshift 内部视图完成的)。找到 UPDATE 后,复制修复程序将对其进行解析以查找用于 UPDATE 的 AWS 中间表,然后提取其名称、目标表和正在更新的行范围。然后,它通过在中间表和目标表之间执行 LEFT JOIN 来查找丢失的行(或多行)。找到 ID 后,修复程序会从源数据库中检索丢失的行。
使用实际行,修复程序生成 INSERT 语句,相应地将数据类型从 PostgreSQL 转换为 Redshift。生成 INSERT 语句后,修复程序会针对 Redshift 集群执行它。最后,复制任务停止并恢复。之后,检查员等待 15 分钟再次检查。修复程序,深入 Redshift 内部视图以获取丢失行的 ID 数据类型转换器——Redshift 并非支持所有 PostgreSQL 数据类型(数组、JSON 等)。该组件将这些类型转换为可以插入 Redshift 表的内容。通常,Redshift 会自动执行维护窗口。您可以选择此维护窗口的发生时间,甚至可以推迟它。为什么这很重要?过去,我们遇到过一些极端情况,即维护窗口在 UPDATE 语句中间重启 Redshift 集群,导致 DMS 任务静默失败(这意味着任务没有被标记为失败,但在后台它停止了在职的)。我们创建了一个工具,可以在指定时间停止所有配置的复制任务,并在 35 分钟后恢复它们。您可能想知道:为什么是 35 分钟?假设您在 UTC 时间周三上午 11:00 设置了每周维护窗口。我们注意到通常维护窗口的工作方式如下:
我们创建的工具在 10:55 AM UTC 停止所有复制任务,并在 11:30 AM UTC 恢复所有复制任务。这就是神奇数字(35 分钟)的来源。我们认为我们可以更早地恢复任务,但我们更愿意等到 UTC 时间上午 11:30 以防止出现任何中断,以防 Redshift 将其维护窗口移动几分钟。 DMS 似乎有内存泄漏。我们不能肯定,因为 DMS 是一个黑盒产品,所以我们对其内部结构一无所知。事实上,DMS 复制实例每周会出现两次内存不足的情况。我们创建了一个工具,用于检查实例中剩余多少内存,如果可用内存量低于 1.5 GB,则停止并恢复任务。这会导致实例内存反弹到接近实例最大值的值。以前,我们是重启实例而不是停止和恢复任务,但我们意识到重启实例会导致数据丢失。正如我们之前所讨论的,在某些情况下,DMS 无法复制更改。据我们所知,这些场景是: 有时它只是发生。我们不知道为什么。不过,它并不是每天都发生。由于我们无法控制此过程,因此我们创建了一个工具,每天检查以确保给定日期的所有源数据库行都成功复制到 Redshift。
这是有效的,因为我们正在复制的所有表都有单调递增的主键,我们可以利用这些主键来绑定哪些 ID 属于给定的一天。对于 DMS 中配置的每个表,我们在目标数据库中搜索主键的最大值和最小值。我们将所有这些标识符从源文件转储到一个文件中,并将它们加载到 Redshift 中。在 Redshift 中获得一天的所有 ID 后,我们执行 LEFT JOIN 以查找丢失的 ID。对于每个缺失的行,都会生成一个 INSERT 语句,同时考虑 PostgreSQL 数据类型如何映射到 Redshift。创建 DMS 复制任务时,您可以通过在目标数据库中手动创建表来指定目标表结构,也可以让 DMS 为您创建表。根据我们的经验,DMS 自动选择的压缩编码可能不是最佳的。如果您不确定要使用哪种编码,合理的方法是:创建一个 PoC/Development Redshift 集群,用作安全的目标。
使用要复制的目标表在完全加载模式下创建 DMS 复制任务。运行 DMS 复制任务。如果您有大量桌子,请让任务运行一个小时或更长时间,以便填满桌子。当完全加载完成或您在几小时后停止加载时,运行 ANALYZE COMPRESSION 语句。提醒:ANALYZE COMPRESSION 是一项繁重的操作,不应在生产环境中运行。如文档中所述:“ ANALYZE COMPRESSION 获取排他表锁,以防止对表进行并发读取和写入。仅在表空闲时运行 ANALYZE COMPRESSION 命令。”完成 ANALYZE COMPRESSION 语句后,您可以在 Redshift 中为目标表生成 SQL 创建脚本,并在复制任务开始之前在生产集群中运行它。经过一年的生产使用,我们认为 AWS DMS 是一个有趣的产品,具有很多潜力,但在某些方面并不成熟。它帮助我们将生产 PostgreSQL 集群数据移动到 Redshift,这是我们之前尝试过几次但没有成功的方法。我们仍然在内部工具上花费了大量的工程时间,但我们认为这是值得的。感谢 AWS 更新和我们的内部工具,我们找到了一种让 DMS 为我们工作的方法,我们现在觉得我们可以在生产环境中安全地使用它。如果您正在考虑使用 DMS 作为复制工具,我们希望您发现这篇文章有用。我们专注于从 PostgreSQL 复制到 Redshift 的具体细节,但我们认为我们在此过程中学到的许多经验教训可以应用于其他用例。