Debezium发布历史84

原文地址: https://debezium.io/blog/2020/02/25/lessons-learned-running-debezium-with-postgresql-on-rds/

欢迎关注留言,我是收集整理小能手,工具翻译,仅供参考,笔芯笔芯.

在 Amazon RDS 上使用 PostgreSQL 运行 Debezium 的经验教训
二月 25, 2020 作者: 阿什哈尔·哈桑
AWS Postgres RDS
在这篇博文中,我们将讨论印度领先的供应链服务公司Delhivery如何使用 Debezium 为许多不同的业务用例提供支持,包括驱动事件驱动的微服务、提供数据集成以及将运营数据移动到用于实时分析和报告的数据仓库。我们还将看看我们在集成 Debezium 时犯的早期错误以及我们如何解决这些错误,以便任何未来的用户都可以避免它们,讨论我们面临的更具挑战性的生产事件之一,以及 Debezium 如何帮助确保我们可以在没有任何数据的情况下恢复损失。最后,我们讨论 Debezium 为我们提供了哪些价值、我们认为还有改进空间的领域以及 Debezium 如何适应我们未来的目标。

Debezium 在 Delhivery
我们在物流领域工作,因此我们编写的大多数软件都专注于货物的状态变化、跟踪位置更新、收集实时数据并对其做出反应。在任何软件架构中最常见的地方就是数据库。我们主要在 MongoDB 等文档数据库和关系数据库系统(特别是 PostgreSQL)中维护所有交易数据,以用于组织内的不同服务。需要对所有不同数据源的事务数据进行高效且近乎实时的分析,以便获得洞察并了解组织的运作方式并做出数据驱动的决策。

为了实现上述目标,我们使用 Debezium 对事务数据执行变更数据捕获,使其在我们选择的消息代理 Kafka 中可用。一旦 Kafka 中的数据可用,我们就可以执行以下一项或全部操作:

跨不同关系表的变更流执行流式联接或数据丰富(甚至可能来自不同的数据库或服务。例如,使用行程和车辆数据丰富货运)

从变更流创建域事件以供下游服务使用(例如,使用来自三个不同变更流的订单、发货和产品信息创建聚合消息)

将变更流数据移动到数据湖中以允许灾难恢复或重放部分数据

复杂的事件处理,以生成实时指标和强大的仪表板(例如,运输中物品的实时计数、每个区域内的平均行程时间等)

Debezium 通过提供通用平台和框架来连接我们现有的数据源(如 MongoDB、PostgreSQL 或 MySQL),使上述所有用例成为可能,并且非常容易构建。

本文将分享我们在 AWS RDS(AWS 的托管数据库服务)上使用 Debezium 的经验,希望有助于传授我们在此过程中获得的一些知识,并记录如何跳过 PostgreSQL 的 WAL 中的不可解析记录,直到DBZ-1760得到修复(已实施并计划在下一个 Debezium 1.1 预览版中发布)。

以下是一个简短的架构概述,展示了 Debezium 所支持的一些用例和通用数据平台。
图片来自于原文
Debezium发布历史84_第1张图片

图 1. 当前架构

但实现上述目标是一个迭代过程,需要进行大量的实验和反复试验。

Debezium 与 A​​WS RDS 上的 PostgreSQL
现在我们将讨论在 AWS RDS 上运行 Debezium 和 PostgreSQL 的一些经验教训。我们不会重点讨论如何在 RDS 上开始使用 Debezium 和 PostgreSQL,因为PostgreSQL 连接器的文档对此进行了详细记录。

得到教训
我们首先创建一个概念验证,其目标是监听单个 PostgreSQL 数据库中 3 个不同表的更改,并在下游创建两个视图,一个作为三个表的联接,另一个视图包含作为跟踪的聚合指标时间序列。连接和聚合都是使用Kafka Streams实现的,因为与其他流处理框架相比,它更容易设置和学习。由于 Debezium 已经提供了功能非常丰富的Docker 容器映像,因此我们对其进行了稍微扩展,并决定将该服务作为容器在 AWS 的 Elastic Container Service(容器编排服务)上运行。

当我们刚开始时,我们犯了一些错误。我们错误的所有解决方案现在都记录在 Debezium 文档中,但它们都列在一起,以便更容易避免它们。

我们最初使用wal2json插件,该插件导致连接器在提交大型事务(序列化形式使用的内存多于可用 Java 堆空间的事务)时遇到OutOfMemoryError 。因此我们的建议是:

在 PostgreSQL < 10 上,使用wal2json_streaming插件以避免大型事务出现OutOfMemoryError。

在 PostgreSQL >= 10 上,使用pgoutput插件。

我们正在生成启用模式的 JSON 消息;这会创建比需要更大的 Kafka 记录,特别是在架构更改很少的情况下。因此,我们决定通过设置key.converter.schemas.enabled和来禁用消息模式,value.converter.schemas.enabled以false显着减少每个有效负载的大小,从而节省网络带宽和序列化/反序列化成本。唯一的缺点是我们现在需要在外部模式注册表中维护这些消息的模式。

我们正在观察一些使用 Base64 编码数据的数据类型。如文档所述,这是 NUMERIC 列的默认值,但对于消费者来说可能很难处理。为了转换为更容易解析的格式,但会损失一些准确性,我们按照文档配置了数据类型特定属性。具体来说,设置decimal.handling.mode为string以字符串形式接收 NUMERIC、DECIMAL 和等效类型(例如“3.14”),并设置hstore.handling.mode为json以 JSON 字符串形式接收 HSTORE 列。

始终确保基本的卫生检查,例如数据库磁盘使用情况、事务日志磁盘使用情况以及用于读写操作的网络和磁盘带宽。未针对数据库上的事务日志磁盘使用情况设置警报。我们在 RDS 指标TransactionLogsDiskUsage和OldestReplicationSlotLag上添加了警报,以便在事务日志磁盘使用量增加到阈值以上或复制槽开始滞后时向我们发出警报 - 这意味着 Debezium 可能已经死亡。

我们没有在 Debezium 中启用心跳。以下情况需要使用心跳来控制WAL磁盘空间消耗:

受监控的数据库中有许多更新,但只有极少量与受监控的表和/或模式相关。我们通过设置启用心跳来处理这种情况heartbeat.interval.ms。

PostgreSQL 实例包含多个数据库,其中受监控的数据库与其他数据库相比流量较低。由于 WAL 被实例中的所有数据库共享,因此它会不断积累数据,这些数据在 Debezium 读取之前无法删除。但由于高流量数据库不受监控,Debezium 无法与数据库进行通信,以便删除 WAL 文件以回收磁盘空间。为了解决这种情况,我们通过使用以下查询定期更新在受监控数据库中创建的表中的单行来触发“心跳”事件:

CREATE TABLE IF NOT EXISTS heartbeat (id SERIAL PRIMARY KEY, ts TIMESTAMP WITH TIME ZONE);
INSERT INTO heartbeat (id, ts) VALUES (1, NOW()) ON CONFLICT(id) DO UPDATE SET ts=EXCLUDED.ts;
由于这是将 Debezium 与 PostgreSQL 结合使用时出现的常见用例,因此在DBZ-1815中创建了一个问题来跟踪此问题。

具有 JSONB 列的表的吞吐量严重降低。调试后,我们能够确认原因是 Debezium 频繁刷新架构,因为复制消息中不存在 TOAST 列。schema.refresh.mode通过更改为解决了此问题columns_diff_exclude_unchanged_toast,并已记录在案。

我们观察到一些 RDS 实例大小的数据库连接上频繁出现 EOF 错误。我们仍然不确定原因,但初步调查表明该问题仅发生在附加了 PgBouncer 的实例(即使未通过 PgBouncer 连接)或较小尺寸的实例(AWS t2/t3 系列)上。

我们最初为每个 PostgreSQL 数据库(而不是每个主机)使用一个 Debezium 连接器,但后来改为为每个团队使用一个连接器。不为每个 PostgreSQL 实例运行单个连接器的主要原因是工作负载隔离。任何执行批量数据更新或删除或计划外架构迁移的团队只会影响他们自己的 Debezium 连接器,而不是整个 PostgreSQL 实例,因为 Debezium 根据配置的数据库和/或架构白名单和黑名单在其末尾过滤事件。我们正在尝试找出此配置中可能存在的问题,但尚未发现任何问题。迁移到每个团队设置单个连接器还减少了有关配置更改的大量管理开销,因为在为任何更改创建发布计划时,我们不再需要在多个团队之间进行协调。尽管单个数据库上的多个复制槽确实会增加开销,但我们可以在每个数据库主机上使用大约 6 到 10 个槽时正常运行,而不会产生任何明显的性能影响。

生产事故
正如每个软件开发项目所常见的那样,我们确实遇到了一些问题,在这里我们详细讨论其中一个更困难的问题。但由于 Debezium 专注于确保数据一致性,我们能够在没有任何数据丢失的情况下进行恢复。

我们下面讨论的问题已经在 Debezium 1.0 中修复,您应该尽快更新。

通常跳过此类不可处理事件的新功能已作为PR#1271合并到核心 Debezium 框架中,并将成为下一个 Debezium 1.1 预览版的一部分。

开发人员经常未能做到的两件事是正确的日期时间处理和软件版本升级。这两种情况各自都会导致问题,但当两者同时出现时,事情就会变得困难。我们最近遇到了这样的问题并提供了一种处理方法。我们将从一些背景知识开始,解释为什么这个问题首先出现。

PostgreSQL 的日期/时间类型文档指出 TIMESTAMP 类型的范围可以从4713 BC到294276 AD。在 Debezium 0.10 之前,存在一些关于日期时间溢出的问题,例如DBZ-1255和DBZ-1205 等太远的未来日期。

Bug 及其处理
要解决上述问题,您需要有一个足够远的未来日期。如果您不使用 ISO8601 或纪元时间并且自定义日期时间格式化程序中存在错误,则可以获得一个。

因此,该错误是由应用程序将包含20200 年的日期时间值写入 Debezium 监控的表之一触发的,这导致 Debezium 抛出异常,因为我们仍在生产中运行 0.9。

不幸的是,我们的日志模式警报那天不起作用,并且错误默默地跳过了我们,直到高复制滞后警报响起。在检查日志后,我们确实找出了问题的根源以及问题的价值。不幸的是,日志没有告诉问题出在哪个表中(提示 - 可以成为有价值的贡献)以及哪一列包含有问题的值。幸运的是,只监控了四个表,每个表都有两个 TIMESTAMPTZ 列,并且很容易查询这些表中的违规值以找到实际记录。

快速阅读源代码后,我们发现这种情况发生在大于 9999 的任何年份,因此我们查询数据库以检查是否存在任何其他此类值。值得庆幸的是,不存在其他值。到目前为止,我们心里已经有了一个明确的计划:

停止Debezium

更正数据以供记录

以某种方式让 Debezium 跳过无法解析的记录

向数据库添加验证以确保暂时不会跳过这些值

将 Debezium 升级到 1.0

但我们陷入了上面的第三步,因为我们找不到与 MySQLevent.deserialization.failure.handling.mode连接器相同的选项。

Debezium 和 PostgreSQL 如何跟踪偏移量
PostgreSQL 中的每个更改记录都有一个位置,可以使用称为日志序列号 (LSN) 的值来跟踪该位置。PostgreSQL 将其表示为两个十六进制数——逻辑xLog和段。Debezium 将其表示为该值的十进制表示形式。实际的转换实现可以在 PostgreSQL 的 JDBC 驱动程序中看到。

Debezium 定期将最后处理的 LSN 和事务 ID 写入 Kafka Connect 偏移主题,并推进复制槽以匹配它。启动时,Debezium 使用 Kafka Connect 偏移主题中的最后一条记录将复制槽回退到继续流式更改之前描述的位置。这意味着要更改 Debezium 在 WAL 中获取数据的位置,需要更改 Debezium 在 Kafka Connect 偏移量主题中的跟踪信息以及 PostgreSQL 中的服务器端。

跳过不可解析的事件
我们能够使用上述信息通过执行以下步骤来使 Debezium 跳过无法解析的事件:

停止 Debezium 以使复制槽处于非活动状态。

通过运行 检查 Debezium 是否已停止侦听复制槽SELECT * FROM pg_replication_slots WHERE slot_name = ‘’;。该active列应该是f.

检查 Debezium 的 offsets 主题中的最后一条消息并记下该lsn键的值。例如。1516427642656。

使用以下 Java 代码,使用 PosgtreSQL 的 Java 驱动程序将 LSN 的长表示形式转换为十六进制格式:

import org.postgresql.replication.LogSequenceNumber;

class Scratch {
public static void main(String[] args) {
LogSequenceNumber a = LogSequenceNumber.valueOf(1516427642656L);
System.out.println(a.asString());
}
}
使用 来查看从 WAL 到上面的 LSN 的更改SELECT pg_logical_slot_peek_changes(’’, ‘’, 1)。这是我们要跳过的复制更改,因此请确保这是您要跳过的记录。确认后,进行下一步。

通过使用 跳过 1 次更改来推进复制槽SELECT pg_logical_slot_get_changes(’’, NULL, 1)。这将消耗复制槽的 1 次更改。

使用下一个 LSN 和 TxId 将消息发布到 Debezium 的偏移主题。lsn通过向和 都添加 1,我们能够成功地使其工作txId。

再次部署 Debezium,它应该会跳过该记录。

结论
为什么是黛比修姆?
最后,我们想强调 Debezium 为我们解决的问题。

处理任何数据时最关心的问题之一是数据一致性,Debezium 帮助我们避免双重写入并维护 RDBMS 和 Kafka 之间的数据一致性,这使得更容易确保所有后续层的数据一致性。

Debezium 支持低开销的变更数据捕获,现在我们最终默认为所有正在创建的新数据源启用 Debezium。

Debezium 对各种数据源(特别是 PostgreSQL、MySQL 和 MongoDB)的支持帮助我们提供了执行数据集成的标准技术和平台。不再需要编写自定义代码来连接每个数据源。

Debezium 的开源特性在早期被证明非常有用,它确保我们能够自己发送针对一些错误的补丁,而不必要求某人优先考虑问题。由于它是开源的,因此周围有一个不断增长的社区,可以帮助您找出问题并提供一般指导。查看Debezium 网站上的此页面,了解许多精彩的社区贡献内容。

挑战
话虽如此,上述 Debezium 仍然是一个相当年轻的项目,并且有一些值得改进的领域(以及您以代码、设计、想法、文档甚至博客文章的形式做出的贡献):

零停机高可用性。Debezium 依赖 Kafka Connect 框架来提供高可用性,但它不提供类似于热备实例的功能。关闭现有连接器和启动新实例需要时间 - 这对于某些用例来说可能是可以接受的,但在其他用例中是不可接受的。请参阅BlaBlaCar 的这篇博客文章,了解相关讨论及其解决方案。

支持除 Kafka 之外的其他数据接收器。在某些情况下,您可能希望将事件从数据库直接移动到 API、不同的数据存储或不同的消息代理。但由于 Debezium 目前是在 Kafka Connect 之上编写的,因此它只能将数据写入 Kafka。Debezium 确实提供了一个嵌入式引擎,您可以将其用作库来使用 Java 应用程序中的更改事件。请参阅有关嵌入 Debezium 的文档。如果您最终确实围绕 Debezium 编写了不同的适配器以将数据移动到不同的目的地,请考虑将其开源,这样您既可以从额外的维护人员中受益,也可以通过解决新的用例而使社区受益。

用于编写任何新的 CDC 实现的通用框架。我们特别有一个在 AWS DynamoDB 上执行 CDC 的用例。我们可以重用 Debezium 核心框架并仅编写 DynamoDB 特定部分,而不是从头开始编写自定义 Kafka Connector。这将有助于防止错误,因为许多现有流程和边缘情况可能已经得到处理。围绕此主题正在进行的工作是重构所有现有 Debezium 连接器以使用通用框架,从而更轻松地编写新的自定义连接器。有关如何实现的示例,请查看Debezium 孵化器存储库。

项目的问题跟踪器已经跟踪了一些小烦恼 - 特别是DBZ-1760(跳过不可解析的记录)、DBZ-1263(更新现有连接器的表白名单)、DBZ-1723(失败时重新连接到数据库)、DBZ-823 (并行快照)。

未来范围
我们确实为未来计划了一些任务,以改进有关 Debezium 和 Kafka Connect 的现有工作流程。

升级到 Debezium v​​1.0。Debezium 最近发布了第一个 1.0 版本,其中包含许多新功能,包括对 CloudEvents 格式的支持,我们希望为整个组织的所有数据提供统一的消息格式。

尝试使用发件箱模式进行可靠的微服务数据交换中记录的发件箱设计模式,以统一应用程序事件和数据更改事件。发件箱模式还提供了微服务系统中跨服务边界的事务保证——这是每个人都希望在基于事件的微服务架构中实现的。

设置Apache Atlas集成以自动创建数据源并跟踪 Atlas 中的数据沿袭,以帮助数据治理和可发现性。

编写并开源 AWS DynamoDB CDC 连接器作为 Debezium 连接器。由于我们也使用 AWS DynamoDB,因此我们需要提供与其他数据源在 CDC 方面使用的相同功能。为此,我们正在使用 Debezium 作为框架编写 DynamoDB CDC 连接器。这项工作仍处于早期阶段,计划作为开源连接器发布。

总的来说,我们首先分享了我们的业务用例,并讨论了 Debezium 如何帮助我们解决这些问题。然后,我们详细介绍了如何在生产环境中运行 Debezium,以便在 AWS RDS 上的 PostgreSQL 上执行 CDC,并讨论了我们在开始时犯的错误以及如何解决这些错误。正如软件工程中常见的那样,我们一路上确实遇到了生产事件,并分享了我们从该事件中学到的经验教训,希望它们对更广泛的社区有用。

还要非常感谢审阅这篇文章的人,包括Gunnar Morling、Kapil Bharati和Akash Deep Verma。

进一步阅读
Debezium 文档和存储库
Debezium PostgreSQL 连接器文档

Debezium 与 A​​mazon RDS 上的 PostgreSQL

Debezium 嵌入式引擎

Debezium 孵化器连接器 - Cassandra、IBM DB2

外部文档
卡夫卡流

PostgreSQL 日期/时间数据类型

JDBC 驱动程序中的 PostgreSQL LSN 转换

博客和文章
从整体流式传输数据:构建高度可靠的 CDC 堆栈

使用发件箱模式进行可靠的微服务数据交换

相关问题
开放式问题
DBZ-1760 - 添加选项以跳过无法处理的事件

DBZ-1263 - 允许在创建连接器后更新 table.whitelist

DBZ-1815 - Postgres 连接器心跳应该选择将心跳更改写回数据库

已解决的问题
DBZ-1255 - Debezium 预计年份不会大于 9999

DBZ-1205 - Postgres 连接中的时间戳溢出

你可能感兴趣的:(debezium,CDC,FlinkCDC,数据库,运维,大数据)