原文地址: https://debezium.io/blog/2018/05/24/querying-debezium-change-data-eEvents-with-ksql/
欢迎关注留言,我是收集整理小能手,工具翻译,仅供参考,笔芯笔芯.
使用 KSQL 查询 Debezium 更改数据事件
五月 24, 2018 作者: Jiri Pechanec
mysql ksql 示例
最后更新于 2018 年 11 月 21 日(调整为新的 KSQL Docker 映像)。
去年,我们见证了Apache Kafka领域中一个新的开源项目KSQL的启动,它是一个构建在Kafka Streams之上的流式 SQL 引擎。在这篇文章中,我们将尝试使用 Debezium 从 MySQL 数据库生成的数据更改事件进行 KSQL 查询。
作为数据源,我们将使用教程中的数据库和设置。此练习的结果应该类似于最近关于将事件聚合到域驱动聚合中的帖子。
实体图
首先让我们看一下数据库中的实体以及它们之间的关系。
图片来自于官网
图 1:示例实体的实体图
上图显示了示例 MySQL 实例中库存数据库的完整 ER 图。我们将重点关注两个实体:
customers- 系统中的客户列表
orders- 系统中的订单列表
和之间存在1:n关系,由表中的列建模,该列是表的外键。customersorderspurchaserorderscustomers
配置
我们将使用Docker Compose 文件来部署环境。该部署由以下 Docker 映像组成:
阿帕奇动物园管理员
阿帕奇·卡夫卡
Kafka Connect 包括 Debezium 连接器镜像
我们的教程中使用的预填充 MySQL 数据库
KSQL 服务器和CLI客户端
例子
首先我们需要启动 Debezium 和 Kafka 基础设施。为此,请克隆debezium-examples GitHub 存储库并使用提供的 Compose 文件启动所需的组件:
export DEBEZIUM_VERSION=0.8
git clone https://github.com/debezium/debezium-examples.git
cd debezium-examples/ksql/
docker-compose up
接下来我们必须注册 Debezium MySQL 连接器的实例来监听数据库中的更改:
curl -i -X POST -H "Accept:application/json" -H "Content-Type:application/json" http://localhost:8083/connectors/ -d @- <<-EOF
{
"name": "inventory-connector",
"config": {
"connector.class": "io.debezium.connector.mysql.MySqlConnector",
"tasks.max": "1",
"database.hostname": "mysql",
"database.port": "3306",
"database.user": "debezium",
"database.password": "dbz",
"database.server.id": "184055",
"database.server.name": "dbserver",
"database.whitelist": "inventory",
"database.history.kafka.bootstrap.servers": "kafka:9092",
"database.history.kafka.topic": "schema-changes.inventory",
"transforms": "unwrap",
"transforms.unwrap.type": "io.debezium.transforms.UnwrapFromEnvelope",
"key.converter": "org.apache.kafka.connect.json.JsonConverter",
"key.converter.schemas.enable": "false",
"value.converter": "org.apache.kafka.connect.json.JsonConverter",
"value.converter.schemas.enable": "false"
}
}
EOF
现在我们应该让所有组件启动并运行,并且初始数据更改事件已经流入 Kafka 主题。有多个属性对于我们的用例特别重要:
使用UnwrapFromEnvelope SMT 。这允许我们直接将部分after变更记录中的字段映射到KSQL语句中。如果没有它,我们将需要使用从消息部分EXTRACTJSONFIELD中提取的每个字段。after
JSON 转换器禁用架构。原因与上面相同。启用模式后,对于 JSON,记录封装在包含字段schema(带有模式信息)和payload(带有实际数据本身)的 JSON 结构中。我们将再次需要使用EXTRACTJSONFIELD来访问相关字段。Avro转换器不存在此问题,因此使用Avro时无需设置此选项。
接下来我们将启动 KSQL 命令 shell。我们将在 CLI 中运行本地引擎。另请注意--net参数。这保证了 KSQL 容器与 Debezium 容器在同一网络中运行,并允许正确的 DNS 解析。
docker-compose exec ksql-cli ksql http://ksql-server:8088
首先,我们将列出代理中存在的所有 Kafka 主题:
ksql> LIST TOPICS;
Kafka Topic | Registered | Partitions | Partition Replicas
connect-status | false | 5 | 1
dbserver | false | 1 | 1
dbserver.inventory.addresses | false | 1 | 1
dbserver.inventory.customers | false | 1 | 1
dbserver.inventory.orders | false | 1 | 1
dbserver.inventory.products | false | 1 | 1
dbserver.inventory.products_on_hand | false | 1 | 1
ksql__commands | true | 1 | 1
my_connect_configs | false | 1 | 1
my_connect_offsets | false | 25 | 1
schema-changes.inventory | false | 1 | 1
我们感兴趣的主题是dbserver.inventory.orders和dbserver.inventory.customers。
默认情况下,KSQL 处理从偏移量开始latest。我们想要处理主题中已有的事件,因此我们从earliest偏移量切换处理。
ksql> SET 'auto.offset.reset' = 'earliest';
Successfully changed local property 'auto.offset.reset' from 'null' to 'earliest'
首先,我们需要从包含 Debezium 数据更改事件的主题创建流。KSQL 和 Kafka Streams 术语中的流是没有状态的无限传入数据集。
ksql> CREATE STREAM orders_from_debezium (order_number integer, order_date string, purchaser integer, quantity integer, product_id integer) WITH (KAFKA_TOPIC='dbserver.inventory.orders',VALUE_FORMAT='json');
Message
Stream created
ksql>
ksql> CREATE STREAM customers_from_debezium (id integer, first_name string, last_name string, email string) WITH (KAFKA_TOPIC='dbserver.inventory.customers',VALUE_FORMAT='json');
Message
Stream created
分区
我们的部署每个主题仅使用一个分区。在生产系统中,每个主题可能有多个分区,我们需要确保属于聚合对象的所有事件最终都位于同一分区中。在我们的例子中,自然分区是按客户 ID 进行的。我们将orders_from_debezium根据purchaser包含客户 ID 的字段对流进行重新分区。重新分区后的数据被写入新的主题中ORDERS_REPART:
ksql> CREATE STREAM orders WITH (KAFKA_TOPIC='ORDERS_REPART',VALUE_FORMAT='json',PARTITIONS=1) as SELECT * FROM orders_from_debezium PARTITION BY PURCHASER;
Message
Stream created and running
ksql> LIST TOPICS;
Kafka Topic | Registered | Partitions | Partition Replicas
...
ORDERS_REPART | true | 1 | 1
...
我们也将为客户执行相同的操作。这是必要的,原因有两个:
当前键是一个结构体,其中包含一个id以客户 ID 命名的字段。这与重新分区的订单主题不同,后者仅包含id值作为键,因此分区不会匹配。
当我们稍后创建 JOIN 时,有一个限制,要求键与表中的键字段具有相同的值。表字段包含一个普通值,但键包含一个结构,因此它们不匹配。有关更多详细信息,请参阅此 KSQL 问题。
ksql> CREATE STREAM customers_stream WITH (KAFKA_TOPIC='CUSTOMERS_REPART',VALUE_FORMAT='json',PARTITIONS=1) as SELECT * FROM customers_from_debezium PARTITION BY ID;
Message
Stream created and running
ksql> LIST TOPICS;
Kafka Topic | Registered | Partitions | Partition Replicas
...
CUSTOMERS_REPART | true | 1 | 1
...
为了验证记录是否具有新密钥并因此重新分区,我们可以发出一些语句来比较结果:
ksql> SELECT * FROM orders_from_debezium LIMIT 1;
1524034842810 | {"order_number":10001} | 10001 | 16816 | 1001 | 1 | 102
LIMIT reached for the partition.
Query terminated
ksql> SELECT * FROM orders LIMIT 1;
1524034842810 | 1001 | 10001 | 16816 | 1001 | 1 | 102
LIMIT reached for the partition.
Query terminated
第二列包含ROWKEY消息的关键字。
客户/订单加盟
到目前为止,我们仅将流声明为无界无状态数据集。在我们的用例中,这order实际上是一个来来去去的事件。但是customer是一个可以更新的实体,通常是系统状态的一部分。这种质量在 KSQL 或 Kafka Streams 中以表的形式表示。我们将从包含重新分区客户的主题创建一个客户表。
ksql> CREATE TABLE customers (id integer, first_name string, last_name string, email string) WITH (KAFKA_TOPIC='CUSTOMERS_REPART',VALUE_FORMAT='json',KEY='id');
Message
Table created
现在,我们已准备就绪,可以在客户及其订单之间进行联接,并创建一个查询来监视传入订单并将其与关联的客户字段一起列出。
ksql> SELECT order_number,quantity,customers.first_name,customers.last_name FROM orders left join customers on orders.purchaser=customers.id;
10001 | 1 | Sally | Thomas
10002 | 2 | George | Bailey
10003 | 2 | George | Bailey
10004 | 1 | Edward | Walker
让我们对数据库进行一些更改,这将导致 Debezium 发出相应的 CDC 事件:
docker-compose exec mysql bash -c 'mysql -u M Y S Q L U S E R − p MYSQL_USER -p MYSQLUSER−pMYSQL_PASSWORD inventory'
mysql> INSERT INTO orders VALUES(default,NOW(), 1003,5,101);
Query OK, 1 row affected, 1 warning (0.02 sec)
mysql> UPDATE customers SET first_name='Annie' WHERE id=1004;
Query OK, 1 row affected (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> UPDATE orders SET quantity=20 WHERE order_number=10004;
Query OK, 1 row affected (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 0
您可能会注意到,只有orders表中的更改才会触发连接流中的更改。这是流/表连接的产物。如果任何输入流被修改,我们将需要一个流/流连接来触发更改。
所以数据库修改后select的最终结果是
10001 | 1 | Sally | Thomas
10002 | 2 | George | Bailey
10003 | 2 | George | Bailey
10004 | 1 | Edward | Walker
10005 | 5 | Edward | Walker
10004 | 20 | Edward | Walker
概括
我们已经成功启动了一个KSQL实例。我们已将 KSQL 流映射到 Debezium 填充的 Debezium 主题,并在它们之间建立连接。我们还讨论了流应用程序中的重新分区问题。
如果您想使用 Avro 编码和模式注册表尝试此示例,那么您可以使用我们的Avro 示例。另外,有关更多详细信息和更高级的用法,请参阅 KSQL语法参考。
如果您需要帮助、有功能请求或想分享您对此示例的体验,请在下面的评论中告诉我们。