Shopify的工程团队最近改进了Debezium MySQL连接器,使其支持在没有连接器写访问的情况下对数开云体育官方注册网址据库进行增量快照,而在将Debezium指向只读副本时,连接器是必需的。开云体育电动老虎机此外,Debezium MySQL开云体育官方注册网址连接器现在还允许在增量快照期间更改模式。这篇博文解释了这些特性的实现细节。

为什么只读?

开云体育官方注册网址Debezium添加了增量快照特性在Netflix宣布发布1.6版之后他们的变更数据捕获框架.Shopify,我们使用Deb开云体育官方注册网址ezium进行变更数据捕获(CDC),我们期待着成为早期的采用者。此外,我们希望有一个无写和无锁的解决方案。

不写入解决方案允许从读取副本捕获更改,并提供了CDC不会在数据库端导致数据损坏的最高保证。开云体育电动老虎机

在过去,我们不得不协调快照和迁移,因为模式迁移阻塞已经影响了其他项目的开发。解决方案是只在周末运行快照,因此,我们尽可能少地执行快照。我们也看到了改善这部分过程的机会。

这篇博文深入研究了只读增量快照实现的技术细节,包括MySQL连接器中增量快照期间无锁模式更改的处理。

增量快照

Debezium中的增量快照开云体育官方注册网址博客文章详细介绍了默认实现。该算法利用两种类型信号的信令表:

  1. snapshot-window-open / snapshot-window-close作为水印

  2. execute-snapshot作为触发增量快照的一种方式

对于只读场景,我们需要用替代方案替换这两种类型的信号。

显示高水位和低水位的MASTER STATUS

该解决方案是特定于MySQL,并依赖于全局事务标识符(gtid).因此,需要设置gtid_mode并配置数据库以保持GTID顺序,如果开云体育电动老虎机您正在从read副本中读取。

先决条件:

如果replica_parallel_workers > 0 set replica_preserve_commit_order = ON,则强制执行gtid_consistency = ON

该算法运行一个显示主机状态查询块选择前后执行的GTID设置:

低水位= executed_gtid_set高水位= executed_gtid_set低水位

在只读实现中,水印有一种形式的GTID集,例如:2174 b383 - 5441 - 11 - e8 b90a c80aa9429562:1 - 3, 24 da167 - 0 - c0c - 11 - e8 - 8442 - 00059 - a3c7b00:1 - 19所示

这样的水印不会出现在binlog流中。相反,该算法将每个事件的GTID与内存中的水印进行比较。该实现确保没有过时的读取,并且块只具有不超过低水位事件的更改。

带只读水印的重复数据删除算法

在伪代码中,删除从binlog中读取的事件和通过快照块检索的事件的算法如下所示:

(1)暂停日志事件处理(2)GtidSet lwGtidSet: = executed_gtid_set从显示主状态(3)块:=选择下一块表(4)GtidSet hwGtidSet: =从显示主状态减去executed_gtid_set lwGtidSet(5)恢复日志事件处理inwindow: = false / /事件处理的其他步骤循环而真正做e: =下一个事件从更新日志添加e outputbuffer如果不是inwindow那么如果不是lwGtidSet.contains (e.gtid) / /达到低水印inwindow:如果其他= truehwGtidSet.contains(e.gtid) //还没有达到高水位如果chunk中包含e.gtid,则从chunk中删除e.g key否则// chunk中每一行都达到高水位do追加行到outputbuffer //事件处理循环的其他步骤

水印检测

一个开云体育电动老虎机数据库事务可以更改几行。在这种情况下,多个binlog事件将具有相同的GTID。由于gtid不是唯一的,它会影响计算块选择窗口的逻辑。当水印的GTID集不包含其GTID时,事件更新窗口状态。在事务完成和心跳等事件之后,将不会再有任何具有相同GTID的binlog事件。对于这些事件,只要达到水印的上限就足以触发窗口的打开/关闭。

图1。块选择窗口

重复数据删除发生在块选择窗口内,就像默认实现一样。最后,算法在高水位后插入一个重复数据块:

图2。chunk重复数据删除

不更新所包含的表

接收binlog事件对于快照取得进展至关重要。算法检查的gtid所有事件和未包含的表一起。

无binlog事件

MySQL服务器在复制连接空闲x秒后发送一个心跳事件。只读实现利用binlog更新速率较低时的心跳。

heartbeat的GTID与最新的binlog事件相同。因此,对于心跳来说,达到高水位的上界就足够了。

该算法使用server_uuidheartbeat的GTID的一部分,以从高水位获取最大事务id。该实现确保高水印包含单个server_uuid.一个不变server_uuid允许避免窗口过早被心跳关闭的情况。如下图为例:

图3。这种情况下,窗口可能会因心跳而过早关闭

不需要对低水位进行心跳比较,因为窗口是否打开并不重要。当高水位和低水位之间没有新事件时,这简化了检查。

水印之间没有变化

当块选择过程中没有binlog事件时,binlog事件可以立即打开和关闭窗口。在这种情况下,高水位将是一个空集。在这种情况下,快照块将在低水位之后插入,不需要重复数据删除。

图4。一个空块选择窗口

基于Kafka主题的信号

开云体育官方注册网址Debezium支持通过对信令表的插入触发的临时增量快照。只读的替代方案是通过特定的Kafka主题发送信号。消息的格式模仿信令表的结构。执行快照Kafka消息包括这些参数

  • 数据收集-要捕获的表列表

  • 类型-设置为INCREMENTAL

例子:

关键字:dbserver1值:{"type":"execute-snapshot","data": {"data-collections":["目录。orders"], "type": "INCREMENTAL"}}

MySQL连接器的配置有一个新的signal.kafka.topic财产。主题必须有一个分区和删除保留策略。

一个单独的线程从Kafka主题中检索信号消息。Kafka消息的键需要与设置中的连接器名称相匹配开云体育电动老虎机database.server.name.连接器将跳过与日志条目中的连接器名称不对应的事件。消息键检查允许为多个连接器重用一个信号主题。

当增量快照正在运行时,连接器的偏移量包括增量快照上下文。只读实现将Kafka信号偏移量添加到增量快照上下文中。保持偏移量的跟踪使其在连接器重新启动时不会错过或重复处理信号。

但是,不需要使用Kafka来执行只读增量快照和默认值execute-snapshot写入信号表的信号也可以工作。展望未来,一个用于触发临时增量快照的REST API也可能被设想,要么通过Debezium Server公开,要么作为部署到Kafka Connect的额外REST资源。开云体育官方注册网址

模式在增量快照期间更改

Debe开云体育官方注册网址zium MySQL连接器允许在增量快照期间更改模式.连接器将在增量快照期间检测模式更改,并重新选择当前块以避免锁定ddl。

注意,不支持对主键的更改,如果在增量快照期间执行,可能会导致错误的结果。

历史化的Debeziu开云体育官方注册网址m连接器(如MySQL)可以解析数据定义语言(DDL)等事件ALTER TABLE从binlog流。连接器保留每个表的模式的内存表示,并使用这些模式产生适当的更改事件。

增量快照实现使用了两次binlog模式:

  1. 在从数据库中选择数据块时开云体育电动老虎机

  2. 在块插入到binlog流的时刻

数据块的模式必须同时匹配binlog模式。让我们详细探讨算法如何实现模式匹配。

在选择时匹配chunk和binlog模式

当增量快照查询数据库时,行具有表的最新模式。开云体育电动老虎机如果binlog流落后,内存中的模式可能与最新的模式不同。解决方案是等待连接器在binlog流中接收DDL事件。之后,连接器可以使用缓存表的结构来生成正确的增量快照事件。

快照块是使用JDBC API选择的。ResultSetMetaData存储块的模式。挑战在于ResultSetMetaData中的模式和binlog DDL中的模式具有不同的格式,因此很难确定它们是否相同。

该算法使用两个步骤来获取匹配的基于resultset和基于ddl的模式。首先,连接器在低水位和高水位之间查询表的模式。只要连接器检测到窗口关闭,binlog模式就会更新到ResultSetMetaData。之后,连接器查询数据库以验证模式是否保持不变。开云体育电动老虎机如果模式已更改,则连接器将重复该过程。

该算法将匹配的ResultSet和binlog模式保存在内存中,以允许连接器将每个块的模式与缓存的ResultSet模式进行比较。

当数据块的模式与缓存的ResultSet模式不匹配时,连接器将丢弃所选数据块。然后,算法重复匹配ResultSet和binlog模式的验证过程。之后,连接器从数据库中重新选择相同的块:开云体育电动老虎机

图5。Binlog模式在块选择时不匹配块模式

在插入时匹配块和binlog模式

DDL事件还会触发对受影响表的块重读。重读可以防止出现块的模式比窗口关闭时的binlog流的模式更旧的情况。例如,下图说明了在模式更改之前发生的块选择:

图6。Binlog模式在数据块插入时不匹配数据块模式

演示

我们将使用标准教程部署演示只读的临时增量快照。我们正在使用MySQL作为源数据库。开云体育电动老虎机对于这个演示,您需要打开多个终端窗口。

首先,我们将开始部署,创建Kafka主题,并启动连接器:

启动部署export DEBEZIUM_VERSION=1.9 docker-compose- f docker-compose-m开云体育官方注册网址ysql。yaml up # Terminal 2 #启用execute_gtid_consistency和gtid_mode docker-compose -f docker-compose-mysql。yaml exec mysql bash -c 'mysql -p$MYSQL_ROOT_PASSWORD inventory -e "SET GLOBAL enforce_gtid_consistency=ON;SET GLOBAL gtid_mode=OFF_PERMISSIVE;SET GLOBAL gtid_mode=ON_PERMISSIVE;SET GLOBAL gtid_mode=ON;确认docker-compose -f docker-compose-mysql。MYSQL_ROOT_PASSWORD目录-e显示全局变量\"%GTID%\";"创建一个信令主题docker-compose -f docker-compose-mysql。yaml exec卡夫卡卡夫卡/ bin / kafka-topics.sh \——创建\ bootstrap-server卡夫卡:1 \ 9092 \——分区——复制因子1 \主题dbz-signals #启动MySQL连接器,只捕捉客户表,使信号curl - i - x - h - h“application / json接受:”后“application / json - type:”http://localhost: 8083 /连接器/ - d @ - < < EOF{“名称”:“inventory-connector”、“配置”:{“connector.class”:“io.debezium.connector.mysql.MySqlConnector”、“任务。开云体育官方注册网址Max ": "1", 开云体育电动老虎机"数据库。主机名:“mysql”,“数据库”。开云体育电动老虎机端口:“3306”,“数据库”。开云体育电动老虎机用户":"deb开云体育官方注册网址ez开云体育电动老虎机ium", "数据库"。密码":"dbz", "databas开云体育电动老虎机e.server. "id": "184054", "database.server.name": "dbserver1", "database.include.list": "inventory", "database.history.kafka.bootstrap.servers": "kafka:9092", "database.history.kafka.topic": "schema-changes.inventory", "table.include.list": "inventory.customers", "read.only": "true", "incremental.snapshot.allow.schema.changes": "true", "incremental.snapshot.chunk.size": "5000", "signal.kafka.topic": "dbz-signals", "signal.kafka.bootstrap.servers": "kafka:9092" } } EOF

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

MySQL|dbserver1|snapshot在事务中快照1个表的内容[io. debezum .relation . relationalsnapshot changeeventsource]开云体育官方注册网址

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

在客户表docker-compose -f docker-compose-mysql中连续使用Deb开云体育官方注册网址ezium 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 MySQL client docker-compose -f docker-compose-mysql.yaml exec mysql bash -c 'i=0; while true; do mysql -u $MYSQL_USER -p$MYSQL_PASSWORD inventory -e "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-mysql。Yaml exec kafka/ kafka/bin/kafka-console-consumer.sh \——bootstrap-server kafka:9092 \——from-beginning \——属性打印。Key =true \——topic dbserver1.inventory.orders

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

发送信号docker-compose -f docker-compose-mysqlYaml exec kafka/ kafka/bin/kafka-console-producer.sh \——broker-list kafka:9092 \——property "解析。key=true" \——property "key.serializer=org.apache.kafka.common.serialization.StringSerializer" \——property "value.serializer=custom.class.serialization。JsonSerializer" \——property "key.separator=;"\——topic dbz-signals dbserver1;{"type":"execute-snapshot","data": {"data-collections":["库存。orders"], "type": "INCREMENTAL"}}# Check messages for orders table docker-compose -f docker-compose-mysql.yaml exec kafka /kafka/bin/kafka-console-consumer.sh \ --bootstrap-server kafka:9092 \ --from-beginning \ --property print.key=true \ --topic dbserver1.inventory.orders

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

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

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

结论

开云体育官方注册网址Debezium是一个正在积极开发的优秀的变更数据捕获工具,很高兴成为其社区的一员。我们很高兴在Shopify的生产中使用增量快照。如果您有类似的数据库使用限制,请检查只读开云体育电动老虎机增量快照特性。非常感谢我的团队和Debezium团队,没有他们这个项目就不会开云体育官方注册网址发生。

凯特Galieva

凯特是Shopify流媒体平台的高级制作工程师。她住在加拿大多伦多。


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

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

参与

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

Baidu
map