为什么选择基于日志的 CDC

1. 简介

CDC 的全称是 Change Data Capture,在广义的概念上,只要是能捕获数据变更的技术,我们都可以称之为 CDC。目前通常描述的 CDC 技术主要面向数据库的变更,是一种用于捕获数据库中数据变更的技术。CDC 技术的应用场景非常广泛:

  • 数据同步:用于备份,容灾;
  • 数据分发:一个数据源分发给多个下游系统;
  • 数据采集:面向数据仓库 / 数据湖的 ETL 数据集成,是非常重要的数据源。

CDC 的技术方案非常多,目前业界主流的实现机制可以分为两种:

  • 基于轮询的 CDC:用户通常会在数据源表的某个字段中,保存上次更新的时间戳或版本号等信息,然后下游通过不断的查询和与上次的记录做对比,来确定数据是否有变动。这种方式优点是不涉及数据库底层特性,实现比较通用;缺点是要对业务表做改造,且实时性不高,不能确保跟踪到所有的变更记录,且持续的频繁查询对数据库的压力较大。一般用于离线调度查询批处理作业。
  • 基于日志的 CDC:可以通过触发器(Trigger)或者日志(例如 Transaction log、Binary log、Write-ahead log 等)来实现。当数据源表发生变动时,会通过附加在表上的触发器或者 binlog 等途径,将操作记录下来。下游可以通过数据库底层的协议,订阅并消费这些事件,然后对数据库变动记录做重放,从而实现同步。这种方式的优点是实时性高,可以精确捕捉上游的各种变动;缺点是部署数据库的事件接收和解析器(例如 Debezium、Canal 等),有一定的学习和运维成本,对一些冷门的数据库支持不够。

2. 优势

下面是我详细列出基于日志的 CDC 相对于基于轮询的 CDC 的五个优点。

2.1 捕获所有数据变更

通过读取数据库的日志,我们可以获得变更数据以正确顺序的完整列表。对完整变更历史数据感兴趣的用例来说至关重要。相比之下,使用基于轮询的方法,发生在两次轮循之间数据变更可能会丢失。例如,可能会在两次轮询之间插入或者删除记录,在这种情况下,基于轮询的 CDC 永远不会捕获该记录。

与此相关的是另一个话题是停机,例如,CDC 工具停止更新时。使用基于轮询的 CDC,一旦 CDC 工具重新上线,也只会捕获给定记录的最新状态,在停机期间发生的数据变更都会被忽略。基于日志的 CDC 工具能够从关闭之前停止的点位恢复继续读取数据库日志,从而捕获数据变更的完整历史记录。

2.2 事件延迟低且避免增加 CPU 负载

基于轮询的 CDC,我们可能会尝试增加轮询的频率,以降低中间数据变更丢失的概率。虽然在一定程度上有效,但轮询过于频繁可能会导致性能问题(因为用于轮询的查询会导致源数据库负载)。另一方面,增大轮询间隔可以降低 CPU 负载,但这样不仅会导致变更事件的丢失,还会导致数据变更收集延迟的增大。基于日志的 CDC 允许我们近乎实时地对数据变更做出反应,更不需要以花费 CPU 为代价来反复运行轮询查询。

2.3 对数据模型没有影响

基于轮询的 CDC 需要一些标识来识别自上次轮询以来发生变更的记录。因此,捕获表上需要有一些列,例如,可以使用 LAST_UPDATE_TIMESTAMP 列来查找变更的行。在某些情况下可能没问题,但在一些情况下,这种要求可能并不理想。我们需要确保捕获表上时间戳更新的正确性,无论是通过程序写入还是通过触发器触发。此外,如果要对历史创建的一些表进行捕获,可能会比较麻烦,因为这些表不一定有这样的列可供我们捕获。

2.4 可以捕获删除事件

基于轮询的 CDC,我们无法识别自上次轮询以来被删除的记录。这对于类似副本复制的用例来说是一个大问题,我们希望在源数据库和复制目标上拥有相同的数据集,这意味着如果它们已在源数据库中被删除,我们希望目标端中也能被删除。

2.5 可以捕获旧记录状态以及更多元数据

根据源数据库的能力,基于日志的 CDC 可以为更新和删除事件提供旧记录状态(更新前或者删除前的记录状态)。而基于轮询的 CDC 只能获得行当前状态。在一个变更事件中获取旧行的状态对于许多用例来说非常有用,例如,我们可以使用带有新旧值的完整变更数据进行审计。

此外,基于日志的 CDC 通常可以提供 schema 变更流(例如,以 DDL 语句的形式)并提供其他元数据,例如,事务 ID 或应用特定变更的用户。这些事情通常也可以通过基于查询的方法来实现(取决于数据库的能力),不过我还没有真正看到它在实践中完成。

3. 结论

通过上面我们知道基于日志的 CDC 的五个优势。请注意,这并不是说基于轮询的 CDC 没有了应用市场。例如,如果我们可以接受每一小时变更数据的传输,并且丢失一些中间版本的记录也没有问题或者我们就只是想要数据的当前状态,那么完全可以使用基于轮询的 CDC。

但是,如果您对近实时地数据变捕获更感兴趣,并要求不能丢失任何变更事件(包括删除),那么我非常建议您探索基于日志的 CDC 的方案。如果数据源是 MySQL、PostgreSQL、MongoDB 等常见的数据库实现,建议使用 Debezium 来实现变更数据的捕获。

欢迎关注我的公众号和博客:

参考:

赏几毛白!