昨天,我有机会向 达姆施塔特 Java 用户组 介绍了 Debezium 和变更数据捕获 (CDC) 的理念。这是一个很棒的夜晚,有很多有趣的讨论和问题。其中一个问题是:使用像 Debezium 这样的基于日志的变更数据捕获工具,相比于仅仅轮询更新记录,有什么优势?
那么,这两种方法有什么区别呢?通过轮询(或查询)的 CDC,您会反复运行查询(例如通过 JDBC)来检索要捕获的表中的任何新插入或更新的行。相反,基于日志的 CDC 通过响应数据库日志文件(例如 MySQL 的 binlog 或 MongoDB 的 op log)中的任何更改来工作。
由于这个问题已经不是第一次出现了,我决定在这里的博客上提供一个更详尽的回答。这样,如果将来再次出现这个问题,我就可以引用这篇帖子了 :)
那么,废话不多说,以下是我列出的基于日志的 CDC 相对于基于轮询的方法的五大优势。
- 捕获所有数据更改
-
通过读取数据库的日志,您可以获得所有数据更改的完整列表,并按照它们应用的精确顺序排列。这对于许多对记录更改完整历史感兴趣的用例至关重要。相比之下,使用基于轮询的方法,您可能会错过在两次轮询运行之间发生的中间数据更改。例如,一行记录可能在两次轮询之间被插入然后又被删除,在这种情况下,基于轮询的 CDC 将永远无法捕获到这条记录。
与此相关的是停机时间的问题,例如在更新 CDC 工具时。使用基于轮询的 CDC,一旦 CDC 工具恢复联机,只会捕获给定记录的最新状态,而会丢失停机期间对记录发生的任何早期更改。基于日志的 CDC 工具可以在停机前从中继点继续读取数据库日志,从而捕获完整的数据更改历史。
- 事件延迟低,同时避免 CPU 负载增加
-
通过轮询,您可能会倾向于增加轮询尝试的频率,以减少错过中间更新的机会。虽然这在一定程度上可行,但过于频繁的轮询可能会导致性能问题(因为用于轮询的查询会给源数据库带来负载)。另一方面,增加轮询间隔会减少 CPU 负载,但不仅可能导致错过更改事件,还会增加传播数据更改的延迟。基于日志的 CDC 允许您几乎实时地响应数据更改,而无需花费 CPU 时间反复运行轮询查询。
- 不影响数据模型
-
轮询需要一些指示来识别自上次轮询以来已更改的记录。因此,所有要捕获的表都需要有一个像 `LAST_UPDATE_TIMESTAMP` 这样的列,可以用来查找已更改的行。在某些情况下,这可能没问题,但在其他情况下,这种要求可能不合时宜。具体来说,您需要确保要由写入应用程序或通过触发器捕获的所有表的更新时间戳都得到正确维护。
- 可以捕获删除操作
-
自然,轮询无法让您识别自上次轮询以来被删除的任何记录。对于复制类用例来说,这通常是一个问题,因为您希望在源数据库和复制目标上拥有相同的数据集,这意味着如果记录在源数据库中被删除,您也希望在接收端删除这些记录。
- 可以捕获旧记录状态和其他元数据
-
根据源数据库的功能,基于日志的 CDC 可以为更新和删除事件提供旧记录状态。而通过轮询,您只能获得当前行状态。在单个更改事件中提供旧行状态对于许多用例来说可能很有趣,例如,如果您想出于审计目的向应用程序用户显示包含旧列值和新列值的完整数据更改。
此外,基于日志的方法通常可以提供模式更改流(例如以应用的 DDL 语句的形式),并公开其他元数据,如事务 ID 或应用特定更改的用户。这些通常也可以通过基于查询的方法来实现(取决于数据库的功能),尽管我还没有真正见过实际应用。
总结
好了,这就是基于日志的变更数据捕获的五大优势。请注意,这并不意味着基于轮询的 CDC 没有其应用场景。例如,如果您的用例可以满足每小时传播一次更改的要求,并且在它们之间存在的记录的中间版本丢失没有问题,那么它可能是完全可以接受的。
但是,如果您有兴趣几乎实时地捕获数据更改,并确保不丢失任何更改事件(包括删除),那么我强烈建议您探索 Debezium 启用的基于日志的 CDC 的可能性。Debezium 连接器为您处理了所有繁重的工作,也就是说,您不必处理各个数据库的所有底层细节以及从其日志中获取更改的手段。相反,您可以消费 Debezium 生成的通用且基本统一的更改数据事件。
关于 Debezium
Debezium 是一个开源的分布式平台,可以将现有数据库转变为事件流,使应用程序能够几乎即时地看到并响应数据库中已提交的每个行级更改。Debezium 构建在 Kafka 之上,并提供了 Kafka Connect 兼容的连接器,用于监控特定的数据库管理系统。Debezium 将数据更改的历史记录在 Kafka 日志中,这样您的应用程序可以随时停止和重新启动,并可以轻松地消费在未运行时错过的所有事件,确保所有事件都被正确且完整地处理。Debezium 在 Apache 许可证 2.0 下是 开源 的。