Debezium从1.6版开始的主要改进之一是支持开云体育官方注册网址增量快照.在这篇博客文章中,我们将解释这个特性的动机,我们将深入研究实现细节,我们也将展示它的演示。

为什么使用增量快照?

Debezium自诞生以来最大的难点之一是对所捕获表列表更改的所谓最开云体育官方注册网址佳支持。作为用户,您创建一个新的连接器,其中包含一个要捕获的表列表(table.include.list及相关选项);在稍后的时间点,可能需要调整此配置,以便捕获最初不属于CDC的进一步表。如果它足以更改这些表,那么问题就很容易解决了。但是,如果还需要捕获表的现有内容,该怎么办呢?

捕获表中的现有数据传统上是由Debezium在开云体育官方注册网址快照阶段。该阶段在第一个连接器启动时执行一次,其目标是在某个时间点捕获一致的数据(将静止的数据转换为运动中的数据)。这可能是一个相当长的操作,根据定义,它必须完全执行,或者根本不执行——有点像事务语义。这意味着如果由于连接器重新启动而没有完成快照,则必须从头重新执行快照,并且已经完成的所有操作都将被丢弃。另外,在拍摄快照时,在数据库中并行执行的任何数据修改都不会流化,直到快照完成。开云体育电动老虎机对于非常大的快照,这可能会导致数据库资源出现问题,因为在流启动之前开云体育电动老虎机,事务日志必须保持可用。

因此,我们有三个问题需要解决:

  • 如果必须对现有数据进行流处理,则几乎不可能向捕获的表列表中添加其他表

  • 用于一致性快照的长时间运行的进程,不能终止或恢复

  • 更改正在阻塞的数据流,直到快照完成

传统的解决方案

这个问题是众所周知的,随着时间的推移,我们开发了变通方法,并提出了可能的改进和新的解决方案。作为解决方案,一般建议使用多连接器方法。该用户被要求:

  • 停止连接器

  • 创建一个新表以获取新表的快照(使用initial_only快照模式)

  • 完成后,停止新的连接器

  • 重新配置并启动旧连接器,并将新捕获的表添加到列表中

这种方法在某种程度上达到了目的,但非常笨拙,而且上面提到的关于快照一致性的所有问题仍然适用。

下一步是社区为MySQL的Debezium连接器做出贡献开云体育官方注册网址dbz - 175.它基于有多个二进制日志读取器的概念。一个读取器将捕获最初配置的表,而另一个读取器将快照新表,然后从新表中捕获更改。后一种读本会赶上原来的读本,然后它们会和解并合并成一个版本。

代码运行良好,但它从未离开孵化阶段,因为过程本身相当复杂,在极端情况下容易出错。最后但并非最不重要的一点是,这是一种巧妙的方法,但不幸的是,它不能移植到其他连接器。

Watermark-based快照

2019年底,Netflix工程团队宣布他们已经开发了一个内部变更数据捕获框架。他们还提出了一种创新的解决方案,使用水印,在论文中描述DBLog:一个基于水印的变化数据捕获框架Andreas Andreakis和Ioannis Papapanagiotou。

这种方法背后的主要思想是,更改数据流与快照一起连续执行。框架将低水位和高水位插入到事务日志中(通过写入源数据库),在这两点之间,读取快照表的一部分。开云体育电动老虎机如果在窗口期间对相同的记录进行了快照和修改,框架会在水印之间保存开云体育电动老虎机数据库更改的记录,并将它们与快照值进行协调。

这意味着数据是按块进行快照的——在连接器启动时没有冗长的过程,而且在连接器崩溃或受控终止的情况下,快照可以从最后一个完成的块开始恢复。

根据Netflix,该实现是为MySQL和PostgreSQL数据库提供的。开云体育电动老虎机

信号表

在开始讨论Debezium基于水印开云体育官方注册网址的快照方法的实现之前,我们需要绕一个小弯路。

有时,从外部控制Debezium是有用的,因此可以强制它执行一些请求的操作。开云体育官方注册网址让我们假设有必要重新快照一个已经快照的表—所谓的快照特别的快照。用户需要向Debezium发送一个命令来暂停当前操作并执行快照。开云体育官方注册网址出于这个目的,Debezium定义了开云体育官方注册网址这个概念信号,经由信号表.这是一个特殊的表,用于用户和Debezium之间的通信。开云体育官方注册网址开云体育官方注册网址Debezium捕获表,当用户需要执行某个操作时,他们只需将一条记录写入信号表(发送信号)。开云体育官方注册网址Debezium将接收捕获的更改,然后执行所需的操作。

Debezium中的增量快照开云体育官方注册网址

当我们意识到DBLog的快照方法时,我们认为这种方法是通用的,我们也可以尝试在Debezium中采用它。开云体育官方注册网址此外,由于我们在不同的连接器之间共享了大量代码库(使用Debezium连接器框架),我们的目标是在Debezium核心组件中实现它,以便所有连接器都能一次开云体育官方注册网址性受益于该特性。设计和实现是由DDD-3开云体育官方注册网址Debezium设计文件。

Debezium中的增量快照以临时快照的形式提供。开云体育官方注册网址用户不配置连接器来执行快照,而是使用信令机制来发送快照信号,从而触发一组表的快照。所讨论的信号被调用execute-snapshot信号信息格式为:

数据收集:【< table-id-1 >< table-id-2 >< table-id-3 >]}

当请求表快照时,Debezium将执行以下操作:开云体育官方注册网址

  • 获取表中最大的主键;这是快照端点,其值存储在连接器偏移量中

  • 方法所规定的大小,根据主键的总顺序将表拆分为多个块incremental.snapshot.chunk.size配置选项

当查询一个块时,将构建一个动态SQL语句,选择下一个incremental.snapshot.chunk.size记录,其主键大于前一个数据块中的最后一个主键(或第一个数据块的第一个主键),并且小于或等于记录的最大主键。

默认的块大小是1024。为了提高效率,您可以增加该值(将执行更少的快照查询总数),但这应该与增加的缓冲区所需内存消耗相平衡。建议在您自己的环境中进行一些实验,以确定最适合您的情况的设置。

读取数据块是一个稍微复杂的过程:

  • 一个snapshot-window-open信号发送

  • 执行块查询并将块内容读入内存

  • 一个snapshot-window-close信号发送

为什么需要这样做?为什么仅仅查询数据库是不够的?开云体育电动老虎机答案如下图所示:

图1。事务隔离

开云体育官方注册网址Debezium不是唯一访问数据库的进程。开云体育电动老虎机我们可以预期大量进程同时访问数据库,可能会访问当前被快照的相同记录。开云体育电动老虎机如图所示,对数据的任何更改都将根据提交顺序写入事务日志。由于不可能精确地确定块读取事务的时间以识别潜在冲突,因此添加了打开和关闭窗口事件来划分冲突可能发生的时间。开云体育官方注册网址Debezium的任务就是消除这些冲突。

为此,Debezium将块生成的所有开云体育官方注册网址事件记录到缓冲区中。当snapshot-window-open接收到信号,然后检查来自事务日志的所有事件是否属于快照表。如果是,则检查缓冲区是否包含主键。如果是,那么快照事件将从缓冲区中删除,因为这是一个潜在的冲突。由于不可能正确地对快照和事务日志事件进行排序,因此只保留事务日志事件。当snapshot-window-close信号被接收后,缓冲区中的剩余快照事件被发送到下游。

下图展示了这样一个缓冲区是如何工作的,以及事务日志事件在发送到下游之前是如何过滤的:

图2。缓冲在起作用

记录K2、K3和K4已经存在于数据库中。开云体育电动老虎机在快照窗口打开之前,记录K1被插入,K2被更新,K3被删除。这些事件在从日志中读取时被发送到下游。快照窗口打开,其查询将K1、K2和K4选择到缓冲区中。当窗口打开时,从事务日志中检索K4的删除;K4的快照事件从缓冲区中删除,删除事件发送到下游。插入K5和K6,从日志中检索,相应的事件将被触发。根据特定的时间,缓冲区中也可能有它们的读事件(在图像中是K5的情况),这些事件将被丢弃。当快照窗口关闭时,K1和K2的剩余快照事件将从缓冲区中发出。

连接器重启

到目前为止,我们已经演示了使用增量快照的概念,在连接器运行时,如果需要,可以重复地对相同的表进行快照。我们已经展示了它的执行不会停止来自事务日志的流。最后一项是过程的暂停和继续。

当增量快照正在运行时,增量快照上下文将被添加到每个消息偏移量。上下文由三条信息表示:

  • 要快照的表列表,其中第一个是当前快照的表

  • 表的最大主键

  • 发送到下游的增量快照的上一个事件的主键

这三个项目足以在连接器重新启动后(无论是有意重启还是崩溃后)恢复快照。连接器启动后,负责快照的组件从偏移量中读取数据。它初始化其内部状态,并在最后一个处理事件之后恢复快照。请注意,在连接器未运行时插入或更新的任何记录都将通过常规流读取进行处理,即它们不受正在进行的快照的影响。

这种方法确保了流程的健壮性、对重新启动和崩溃的弹性,并最大限度地减少了重新交付事件的数量(至少一次交付语义仍然适用)。

限制

增量快照与初始一致性快照相比,几乎没有什么缺点:

  • 快照表必须包含主键

  • 如果在快照过程中从表中删除了一个事件,那么可能会发生以下情况之一:

    • 一个事件和删除事件被下游消费者接收

    • 只有一个删除事件被接收

  • 如果在快照过程中更新了表中的事件,则可能发生以下情况之一:

    • 一个事件和更新事件被下游消费者接收

    • 一个更新事件和事件被接收(注意相反的顺序)

    • 只有一个更新事件(如果更新发生在将发出事件,导致在重复数据删除期间将丢弃的事件)

一般来说,事件不应该被理解为表中记录的初始状态,而应该被理解为记录在任意时间点的状态。在Debezium中,与传统的初始快照相比,消费者的语义略有变化,虽然它将保证消费者在增量快照完成后收到完整的数据集,但不会有开云体育官方注册网址(快照)事件的所有记录,但它可以更新事件。同样的道理删除事件:消费者必须准备好接收他们以前从未见过的记录的此类事件。

演示

在讨论了一般概念之后,让我们在一个示例中进一步探索。我们将使用我们的标准教程部署演示临时增量快照。我们正在使用PostgreSQL作为源数据库。开云体育电动老虎机对于这个演示,您将需要多个终端窗口。

在一开始,我们将启动部署,创建信令表,并启动连接器:

#终端1 -启动部署#启动部署export DEBEZIUM_VERSION=1.7 docker-compose- f docke开云体育官方注册网址r-compose-postgres。创建一个信令表回显“Create table”目录。Dbz_signal (id varchar(64), type varchar(32), data varchar(2048))"| docker-compose -f docker-compose-postgres。yaml exec - t postgres env PGOPTIONS="——search_path=inventory" bash -c "psql - u $POSTGRES_USER postgres" #启动postgres连接器,只捕获客户表并启用信令curl -i - x POST - h "Accept:application/json" - h "Content-Type:application/json" http://localhost:8083/connectors/ -d @- <

从日志中我们可以看到table.include.list只设置一个表会被快照,客户

connect_1 | 2021-09-24 13:38:21781 INFO Postgres|dbserver1|snapshot在事务中快照1个表的内容[io. debezum .relation . relationalsnapshotchangeevents开云体育官方注册网址ource]

在下一步中,我们将模拟数据库中的连续活动:开云体育电动老虎机

在客户表docker-compose -f docker-compose-postgres中连续使用开云体育官方注册网址Debezium topic中的消息。Yaml exec kafka/ kafka/bin/kafka-console-consumer.sh \——bootstrap-server kafka:9092 \——from-beginning \——属性打印。Key =true \——topic dbserver1.inventory。客户# Terminal 4 # Modify records in the database via Postgres client docker-compose -f docker-compose-postgres.yaml exec postgres env PGOPTIONS="--search_path=inventory" bash -c "i=0; while true; do psql -U $POSTGRES_USER postgres -c \"INSERT INTO customers VALUES(default,'name\$i','surname\$i','email\$i')\"; ((i++)); done"

这个话题dbserver1.inventory.customers接收连续的消息流。现在将重新配置连接器,以捕获订单表:

# Terminal 5 #在捕获的curl中添加订单表-i - x PUT - h "Accept:application/json" - h "Content-Type:application/json" http://localhost:8083/connectors/inventory-connector/config -d @- <
           

和预期的一样,没有用于订单表:

docker-compose -f docker-compose-postgres。Yaml exec kafka/ kafka/bin/kafka-console-consumer.sh \——bootstrap-server kafka:9092 \——from-beginning \——属性打印。Key =true \——topic dbserver1.inventory.orders

现在让我们通过发送一个信号来启动一个增量的临时快照。的快照消息订单表被传递到dbserver1.inventory.orders的话题。的消息客户表交付不间断。

# 5号终端#发送信号回声“插入库存。dbz_signal VALUES ('signal-1', 'execute-snapshot', '{\"data-collections\": [\"inventory.orders\"]}')"| docker-compose -f docker-compose-postgres。yaml exec -T postgres env PGOPTIONS="——search_path=inventory" bash -c "psql -U $POSTGRES_USER postgres" #查看订单表docker-compose -f docker-compose-postgres的消息。Yaml exec kafka/ kafka/bin/kafka-console-consumer.sh \——bootstrap-server kafka:9092 \——from-beginning \——属性打印。Key =true \——topic dbserver1.inventory.orders

中修改任何记录订单表时,这将作为事件或作为更新事件,取决于事情发生的准确时间和顺序。

作为最后一步,让我们终止部署的系统并关闭所有终端:

#关闭集群docker-compose -f docker-compose-postgres。yaml下来

总结

在这篇博客文章中,我们讨论了增量快照概念的动机,正如DBLog论文所介绍的那样。我们回顾了过去用于实现上述功能的方法。然后,我们深入研究了在Debezium中实现这种新颖的快照方法,最后我们尝试在现场使用它。开云体育官方注册网址

我们希望您会发现增量快照很有用,我们期待您的反馈、经验和用例。在以后的博客文章中,我们将讨论对只读数据库增量快照的支持(Debezium MySQL连接器在1.7版中支持),以及如何使用Kafka主题作为信号方式而不是数据库表来触发临时快照。开云体育电动老虎机开云体育官方注册网址

雅罗西克Pechanec

Jiri是Red Hat的软件开发人员(前质量工程师)。他职业生涯的大部分时间都花在Java和系统集成项目和任务上。他住在捷克共和国布尔诺附近。


关于Debe开云体育官方注册网址zium

开云体育官方注册网址Debezium是一个开源的分布式平台,它将现有数据库转换为事件流,因此应用程序几乎可以立即看到并响应数据库中提交的每一个行级更改。开云体育电动老虎机开云体育官方注册网址Debezium是建立在卡夫卡并提供卡夫卡连接监控特定数据库管理系统的兼容连接器。开云体育电动老虎机开云体育官方注册网址Debezium在Kafka日志中记录了数据更改的历史,所以你的应用程序可以在任何时候停止和重新启动,并且可以很容易地使用它没有运行时错过的所有事件,确保所有事件都被正确和完整地处理。开云体育官方注册网址Debezium是开源Apache许可证,版本2.0

参与

我们希望您觉得Debezium有趣开云体育官方注册网址且有用,并愿意尝试一下。在Twitter上关注我们@开云体育官方注册网址debezium在Zulip上和我们聊天,或加入我们的邮件列表与社区对话。所有的代码都是开源的GitHub上,因此在本地构建代码并帮助我们改进现有连接器并添加更多连接器。如果您发现了问题或对我们如何改进Debezium有想法,请告诉我们开云体育官方注册网址记录问题