ksqlDB是事件流数据库,旨在帮助开发人员在ApacheKafka®之上创建流处理应用程序。
在当今世界,利用流处理的应用程序需要一个重量级的体系结构,该体系结构需要将多个分布式系统集成在一起。这些体系结构通常包括用于从外部世界获取事件的连接器或代理、用于存放这些事件的持久存储、用于处理事件的流处理框架和用于向应用程序提供事件聚合的数据库。
不幸的是,这些部件并没有像你希望的那样组装在一起;所有这些系统都很复杂,每个集成都是一个需要解决的小项目。这就像试图用零部件制造一辆汽车,但这些零部件来自不同的制造商,彼此之间互不沟通。
构建流处理应用程序应该并不难。
ksqlDB大大降低了构建流处理应用程序所需的操作复杂性,这使您可以构建实时系统而无需花费大量时间和费用。通过熟悉的轻量级SQL语法,它将实时流处理的功能与数据库的便捷操作的感觉结合在一起。而且由于ksqlDB是由Apache Kafka®提供的本地支持,它无缝地利用了底层的、经过战斗测试的事件流平台。
这些是构建应用程序的基础核心类别:集合,实例化视图和查询。
集合为事件序列提供持久存储。ksqlDB提供两种集合:流和表。两者都在简单的键/值模型下运行。
物化视图是流或表的派生表示形式。它们使您可以在现有流或表上创建新集合。随着新事件的到来,实例化视图将永久保持最新。这意味着您可以将实例化视图链接在一起以创建同一数据的许多表示形式。物化视图对于维护汇总的数据表特别有用。有关更多信息,请参见Materialized Views
物化视图允许您维护状态,而查询使您能够从应用程序或微服务访问这些物化。应用程序可以使用拉式查询和推式查询来查询实例化视图。
参考文章:KSQL Overview
Confluent——KSQL
参考文章:Confluent Platform安装配置和常用操作详细教程
ZooKeeper——用于集群元数据
Kafka——一个或多个
Schema Registry——可选,但如果想使用Avro,必须开启这个
ksqlDB Server——一个或多个
ksqlDB CLI——可选的
Other services——比如Elasticsearch,可选
注:运行模式注册表的堆栈可以处理avro编码的事件。没有模式注册中心,ksqlDB只处理JSON或分隔的事件模式。
可以使用服务器配置文件(ksql-server.properties)
或KSQL_OPTS
环境变量来指定ksqlDB服务器配置参数。使用KSQL_OPTS
设置的属性优先于ksqlDB配置文件中指定的属性。推荐的方法是使用ksqlDB配置文件配置一组公共属性,并根据需要使用KSQL_OPTS
环境变量覆盖特定的属性。
默认情况下,ksqlDB服务器配置文件位于/etc/ksql/ksql-server.properties
。该文件遵循Java属性文件的语法约定。
=
bootstrap.servers=localhost:9092
listeners=http://localhost:8088
开启Ksql Server的方法
<path-to-confluent>/bin/ksql-server-start <path-to-confluent>/etc/ksql/ksql-server.properties
您可以使用KSQL_OPTS
环境变量来覆盖ksqlDB服务器配置参数。这些属性是标准的Java系统属性。例如:
KSQL_OPTS="-Dksql.streams.num.streams.threads=1" <path-to-confluent>/bin/ksql-server-start \
<path-to-confluent>/etc/ksql/ksql-server.properties
您可以同时指定多个参数,例如:
ksql.streams.auto.offset.reset
和ksql.streams.num.stream.threads
:
KSQL_OPTS="-Dksql.streams.auto.offset.reset=earliest -Dksql.streams.num.stream.threads=1" <path-to-confluent>/bin/ksql-server-start \
<path-to-confluent>/etc/ksql/ksql-server.properties
当您运行一个使用Avro的ksqlDB应用程序时,ksqlDB会自动从模式注册表中推断模式,但是重新启动ksqlDB服务器后的行为在交互模式和非交互模式之间有所不同。
如果您的ksqlDB应用程序使用Avro,并且您以非交互模式运行它们,那么请确保模式不会在ksqlDB服务器重新启动时发生变化,或者显式地提供模式。如果模式可能会发展,那么显式地提供模式会更安全。
您可以将ksqlDB CLI连接到每个集群的一个ksqlDB服务器。
注:如果CLI所连接的原始服务器变成不可用的话,则不会将CLI会话自动转移到另一个ksqlDB服务器。您执行的任何持久性查询都将继续在ksqlDB集群中运行。
要将ksqlDB CLI连接到集群,请使用指定的ksqlDB服务器URL运行以下命令(默认为http://localhost:8088
):
这里有一些常见的ksqlDB CLI属性,你可以自定义:
ksql.streams.auto.offset.reset
确定当Apache Kafka®中没有初始偏移量或当前偏移量在服务器上不存在时该做什么。ksqlDB中的默认值是latest
,这意味着所有Kafka主题都是从最新可用偏移量读取的。例如,使用ksqlDB CLI将其更改为earliest
:
SET 'auto.offset.reset'='earliest';
ksql.streams.cache.max.bytes.buffering
用于跨所有线程缓冲的最大内存字节数。ksqlDB中的默认值是10000000 (~ 10mb)。下面是一个使用ksqlDB CLI将值更改为20000000的示例:
SET 'cache.max.bytes.buffering'='20000000';
ksql.streams.num.stream.threads
Kafka Streams应用程序实例中的流线程数。流处理代码在这些线程中运行。有关Kafka流线程模型的更多信息,请参见线程模型。
参考文章:Install ksqlDB
在ksqlDB中,您可以从现有的Apache Kafka®主题创建流,从已存在的Kafka主题创建流或者从其他流创建查询结果流。
CREATE STREAM
语句从现有的Kafka主题或新的Kafka主题创建一个流。CREATE STREAM AS SELECT
语句从现有流创建查询流。注:创建表类似于创建流。
使用CREATE STREAM
语句从现有的Kafka主题创建一个流。该Kafka主题必须已经存在于Kafka集群中。
下面的示例演示如何从名为pageviews的Kafka主题创建流。
1、运行此命令以创建一个名为的主题users
。
<path-to-confluent>/bin/kafka-topics --create --zookeeper localhost:2181 \
--replication-factor 1 --partitions 1 --topic users
输出应为:
Created topic "users".
2、运行此命令以创建一个名为的主题pageviews
。
<path-to-confluent>/bin/kafka-topics --create --zookeeper localhost:2181 \
--replication-factor 1 --partitions 1 --topic pageviews
输出应为:
Created topic "pageviews".
在此步骤中,您将使用Kafka Connect运行名为的演示源连接器kafka-connect-datagen
,该连接器将为Kafka主题pageviews
和创建示例数据users
。
1、运行Kafka Connect Datagen连接器的一个实例,pageviews
以AVRO
格式为主题生成Kafka数据。
wget https://github.com/confluentinc/kafka-connect-datagen/raw/master/config/connector_pageviews_cos.config
curl -X POST -H "Content-Type: application/json" --data @connector_pageviews_cos.config http://localhost:8083/connectors
2、运行Kafka Connect Datagen连接器的另一个实例, users
以AVRO
格式为主题生成Kafka数据。
wget https://github.com/confluentinc/kafka-connect-datagen/raw/master/config/connector_users_cos.config
curl -X POST -H "Content-Type: application/json" --data @connector_users_cos.config http://localhost:8083/connectors
下面的示例创建了一个流,该流包含来自pageviews主题的三个列:viewtime
、userid
和pageid
。
ksqlDB无法推断主题值的数据格式,因此必须提供存储在主题中的值的格式。在本例中,数据格式是DELIMITED
。其他选项有Avro
、JSON
和KAFKA
。有关详细信息,请参阅序列化格式。
ksqlDB要求使用Kafka自己的序列化器或兼容的序列化器对密钥进行序列化。ksqlDB支持INT
、BIGINT
、DOUBLE
和STRING
键类型。
启动ksql
LOG_DIR=./ksql_logs <path-to-confluent>/bin/ksql
首先,创建一个流
CREATE STREAM pageviews
(viewtime BIGINT,
userid VARCHAR,
pageid VARCHAR)
WITH (KAFKA_TOPIC='pageviews',
VALUE_FORMAT='DELIMITED')
EMIT CHANGES;
Message
----------------
Stream created
----------------
查看已创建的流:
SHOW STREAMS;
Stream Name | Kafka Topic | Format
---------------------------------------
PAGEVIEWS | pageviews | DELIMITED
---------------------------------------
获取流的模式:
DESCRIBE PAGEVIEWS;
Name : PAGEVIEWS
Field | Type
--------------------------------------
ROWTIME | BIGINT (system)
ROWKEY | VARCHAR(STRING) (system)
VIEWTIME | BIGINT
USERID | VARCHAR(STRING)
PAGEID | VARCHAR(STRING)
--------------------------------------
For runtime statistics and query details run: DESCRIBE EXTENDED ;
前面的SQL语句没有对底层Kafka主题中的Kafka消息键做任何假设。如果主题中的消息键的值与流中定义的列之一相同,则可以在CREATE STREAM
语句的WITH
子句中指定该键。如果使用这个最新的列名来执行连接或重分区命令,ksqlDB知道不需要重分区。实际上,已命名列成为ROWKEY
的别名。例如,如果Kafka消息键具有与pageid
列相同的值,您可以像这样编写CREATE STREAM
语句:
CREATE STREAM pageviews_withkey
(viewtime BIGINT,
userid VARCHAR,
pageid VARCHAR)
WITH (KAFKA_TOPIC='pageviews',
VALUE_FORMAT='DELIMITED',
KEY='pageid');
使用DESCRIBE EXTENDED
确认新流中的关键字段是pageid:
DESCRIBE EXTENDED pageviews_withkey;
Name : PAGEVIEWS_WITHKEY
Type : STREAM
Key field : PAGEID
Key format : STRING
Timestamp field : Not set - using
Value format : DELIMITED
Kafka topic : pageviews (partitions: 1, replication: 1)
[...]
在ksqlDB中,消息时间戳用于基于窗口的操作,如窗口聚合,并支持事件时间处理。如果您想使用主题列之一的值作为Kafka消息时间戳,请在WITH
子句中设置时间戳属性。
例如,如果您想使用viewtime
列的值作为消息时间戳,您可以将前面的CREATE STREAM
重写为SELECT
语句,如下所示:
CREATE STREAM pageviews_timestamped
(viewtime BIGINT,
userid VARCHAR,
pageid VARCHAR)
WITH (KAFKA_TOPIC='pageviews',
VALUE_FORMAT='DELIMITED',
KEY='pageid',
TIMESTAMP='viewtime')
EMIT CHANGES;
使用DESCRIBE EXTENDED
语句确认TIMESTAMP
字段是viewtime
:
DESCRIBE EXTENDED pageviews_timestamped;
Name : PAGEVIEWS_TIMESTAMPED
Type : STREAM
Key field : PAGEID
Key format : STRING
Timestamp field : VIEWTIME
Value format : DELIMITED
Kafka topic : pageviews (partitions: 1, replication: 1)
[...]
通过在WITH
子句中提供分区(PARTITION
)数和可选的副本(REPLICAS
)数,使用CREATE STREAM
语句创建不存在主题的流。
以上面的pageviews
表为例,但是在Kafka主题不存在的地方,您可以通过将下面的create stream
语句粘贴到CLI中来创建流:
CREATE STREAM pageviews
(viewtime BIGINT,
userid VARCHAR,
pageid VARCHAR)
WITH (KAFKA_TOPIC='pageviews',
PARTITIONS=4,
REPLICAS=3
VALUE_FORMAT='DELIMITED')
EMIT CHANGES;
这将使用你设置的分区数和副本数为你创建pageviews
主题。
使用CREATE STREAM AS SELECT
语句使用现有的流来创建一个持久查询流。
创建一个包含SELECT
查询结果的流。ksqlDB将SELECT
查询结果保存到相应的新主题中。以这种方式创建的流表示的一个持久的、连续的流查询,这意味着它将一直运行,直到您输入命令停止它为止。
注:
SELECT
语句本身是一个非持久的连续查询。SELECT
语句的结果不会持久保存在Kafka主题中,只会打印在ksqlDB控制台中。不要将CREATE STREAM AS SELECT
创建的持久查询与SELECT
语句的流查询结果混淆。
使用SHOW QUERIES
语句来列出当前正在运行的持久查询。使用PRINT
语句在ksqlDB CLI中查看持久查询的结果。按CTRL+C
停止打印记录。当停止打印时,查询将继续运行。
使用TERMINATE
语句来停止持久查询。退出ksqlDB CLI不会停止持久查询。您的ksqlDB服务器将继续处理查询,并且查询将持续运行,直到您显式地终止它们。
要将SELECT
查询的结果流到现有流及其基础主题中,请使用INSERT INTO
语句。
CREATE STREAM AS SELECT
语句不支持键属性。要指定键字段,请使用PARTITION BY
子句。有关更多信息,请参见启用连接的分区数据
下面的SQL语句创建了一个pageviews_intro
流,它包含一个持久查询的结果,该查询与具有小于Page_20
的pageid
值的“介绍性”页面匹配:
CREATE STREAM pageviews_intro AS
SELECT * FROM pageviews
WHERE pageid < 'Page_20'
EMIT CHANGES;
Message
----------------------------
Stream created and running
----------------------------
要确认pageviews_intro
查询是作为一个流连续运行的,请运行PRINT
语句:
PRINT pageviews_intro;
Key format: KAFKA_BIGINT or KAFKA_DOUBLE
Value format: KAFKA_STRING
rowtime: 10/30/18 10:15:51 PM GMT, key: 294851, value: 1540937751186,User_8,Page_12
rowtime: 10/30/18 10:15:55 PM GMT, key: 295051, value: 1540937755255,User_1,Page_15
rowtime: 10/30/18 10:15:57 PM GMT, key: 295111, value: 1540937757265,User_8,Page_10
rowtime: 10/30/18 10:15:59 PM GMT, key: 295221, value: 1540937759330,User_4,Page_15
rowtime: 10/30/18 10:15:59 PM GMT, key: 295231, value: 1540937759699,User_1,Page_12
rowtime: 10/30/18 10:15:59 PM GMT, key: 295241, value: 1540937759990,User_6,Page_15
^CTopic printing ceased
在停止打印流之后,查询将继续运行。
KsqlDB已经确定密钥格式是KAFKA_BIGINT
或KAFKA_DOUBLE
。KsqlDB没有进一步缩小范围,因为仅通过检查密钥的序列化字节不可能排除任何一种格式。在本例中,我们知道键是BIGINT。对于其他情况,您可能知道密钥类型,或者您可能需要与数据的作者交谈。
使用SHOW QUERIES
语句查看ksqlDB为pageviews_intro
流创建的查询:
SHOW QUERIES;
Query ID | Kafka Topic | Query String
CSAS_PAGEVIEWS_INTRO_0 | PAGEVIEWS_INTRO | CREATE STREAM pageviews_intro AS SELECT * FROM pageviews WHERE pageid < 'Page_20' EMIT CHANGES;
For detailed information on a Query run: EXPLAIN ;
由CREATE STREAM AS SELECT
语句创建的持久查询的ID中有字符串CSAS
,例如CSAS_PAGEVIEWS_INTRO_0
。
使用DROP STREAM
语句删除一个流。如果您使用CREATE STREAM AS SELECT
来创建流,那么您必须首先终止相应的持久查询。
使用TERMINATE
语句停止CSAS_PAGEVIEWS_INTRO_0
查询:
TERMINATE CSAS_PAGEVIEWS_INTRO_0;
Message
-------------------
Query terminated.
-------------------
使用DROP STREAM
语句删除持久查询流。在删除相应的流之前,必须终止查询。
DROP STREAM pageviews_intro;
Message
-------------------
Source PAGEVIEWS_INTRO was dropped.
-------------------
在ksqlDB中,您可以从现有的Apache Kafka®主题创建表,创建新Kafka主题的表或者从其他表或流的查询结果创建表。
CREATE TABLE
语句从现有的Kafka主题或新的Kafka主题创建一个表。CREATE TABLE AS SELECT
语句创建一个表,其中包含来自现有表或流的查询结果。使用CREATE TABLE
语句从现有的Kafka主题创建一个表。Kafka主题必须已经存在于Kafka集群中。
下面的示例演示如何从Kafka主题(名为users)创建表。要查看这些示例的实际情况,请使用ksqlDB按照针对Apache Kafka®的写流查询中的过程创建用户主题。
下面的示例创建了一个表,其中有来自用户主题的四列:registertime
、userid
、gender
和regionid
。另外,userid
字段被指定为表的键属性。
键字段是可选的。有关更多信息,请参见Key Requirements。
ksqlDB无法推断主题值的数据格式,因此必须提供存储在主题中的值的格式。在本例中,数据格式是JSON
。其他选项有Avro
、DELIMITED
和KAFKA
。有关更多信息,请参见序列化格式。
ksqlDB要求使用Kafka自己的序列化器或兼容的序列化器对密钥进行序列化。ksqlDB支持INT
、BIGINT
、DOUBLE
和STRING
键类型。
首先,创建一张表
CREATE TABLE users
(registertime BIGINT,
userid VARCHAR,
gender VARCHAR,
regionid VARCHAR)
WITH (KAFKA_TOPIC = 'users',
VALUE_FORMAT='JSON',
KEY = 'userid');
Message
---------------
Table created
---------------
查看表
SHOW TABLES;
Table Name | Kafka Topic | Format | Windowed
----------------------------------------------
USERS | users | JSON | false
----------------------------------------------
获取表的模式
DESCRIBE users;
Name : USERS
Field | Type
------------------------------------------
ROWTIME | BIGINT (system)
ROWKEY | VARCHAR(STRING) (system)
REGISTERTIME | BIGINT
USERID | VARCHAR(STRING)
GENDER | VARCHAR(STRING)
REGIONID | VARCHAR(STRING)
------------------------------------------
For runtime statistics and query details run: DESCRIBE EXTENDED ;
使用SELECT
语句在users
表上创建一个连续的流查询:
SELECT * FROM users EMIT CHANGES;
假设表中有内容,您的输出应该类似于:
+---------------+--------+---------------+--------+--------+----------+
| ROWTIME | ROWKEY | REGISTERTIME | USERID | GENDER | REGIONID |
+---------------+--------+---------------+--------+--------+----------+
| 1541439611069 | User_2 | 1498028899054 | User_2 | MALE | Region_1 |
| 1541439611320 | User_6 | 1505677113995 | User_6 | FEMALE | Region_7 |
| 1541439611396 | User_5 | 1491338621627 | User_5 | OTHER | Region_2 |
| 1541439611536 | User_9 | 1492621173463 | User_9 | FEMALE | Region_3 |
^CQuery terminated
按Ctrl+C停止打印查询结果。
表值使用最新的记录不断更新,因为底层用户主题不断接收新消息。
通过在WITH子
句中提供分区(PARTITIONS
)数和可选的副本(REPLICA
)数,使用CREATE TABLE
语句创建一个不包含预先存在主题的表。
以上面的users
表为例,但是其中的Kafka主题还不存在,您可以通过将以下create table
语句粘贴到CLI中来创建该表:
CREATE TABLE users
(registertime BIGINT,
userid VARCHAR,
gender VARCHAR,
regionid VARCHAR)
WITH (KAFKA_TOPIC = 'users',
VALUE_FORMAT='JSON',
PARTITIONS=4,
REPLICAS=3
KEY = 'userid');
这将使用提供的分区和副本计数为您创建用户主题。
使用CREATE TABLE AS SELECT
语句创建一个ksqlDB表,其中包含来自另一个表或流的SELECT
查询的结果。
CREATE TABLE AS SELECT
使用相应的Kafka主题创建一个新的ksqlDB表,并将SELECT
查询的结果作为一个更改日志流到主题中。ksqlDB创建一个持续运行的查询,直到通过命令终止它为止。
下面的SQL语句创建了一个users_female
表,其中包含性别(gender
)设置为FEMALE
的用户的持久查询结果:
CREATE TABLE users_female AS
SELECT userid, gender, regionid FROM users
WHERE gender='FEMALE'
EMIT CHANGES;
Message
---------------------------
Table created and running
---------------------------
使用SHOW TABLE
和PRINT
语句检查表:
SHOW TABLES;
Table Name | Kafka Topic | Format | Windowed
-------------------------------------------------
USERS | users | JSON | false
USERS_FEMALE | USERS_FEMALE | JSON | false
-------------------------------------------------
打印表中的一些行:
PRINT users_female;
Key format: KAFKA_STRING
Value format: JSON
rowTime: 12/21/18 23:58:42 PM PSD, key: User_5, value: {"USERID":"User_5","GENDER":"FEMALE","REGIONID":"Region_4"}
rowTime: 12/21/18 23:58:42 PM PSD, key: User_2, value: {"USERID":"User_2","GENDER":"FEMALE","REGIONID":"Region_7"}
rowTime: 12/21/18 23:58:42 PM PSD, key: User_9, value: {"USERID":"User_9","GENDER":"FEMALE","REGIONID":"Region_4"}
^CTopic printing ceased
在停止打印表之后,查询将继续运行。
使用SHOW QUERIES
语句查看ksqlDB为users_female
表创建的查询:
SHOW QUERIES;
Query ID | Kafka Topic | Query String
CTAS_USERS_FEMALE_0 | USERS_FEMALE | CREATE TABLE users_female AS SELECT userid, gender, regionid FROM users WHERE gender='FEMALE' EMIT CHANGES;
For detailed information on a Query run: EXPLAIN ;
由CREATE TABLE AS SELECT
语句创建的持久查询的ID中包含字符串CTAS
,例如CTAS_USERS_FEMALE_0
。
使用CREATE TABLE AS SELECT
语句从流中创建表。从流创建表需要聚合,因此需要在SELECT
子句中包含COUNT(*)
这样的函数。
CREATE TABLE pageviews_table AS
SELECT userid, pageid, COUNT(*) AS TOTAL
FROM pageviews_original WINDOW TUMBLING (SIZE 1 MINUTES)
GROUP BY userid, pageid
EMIT CHANGES;
Message
---------------------------
Table created and running
---------------------------
通过使用SELECT
流语句观察表发生的变化。
SELECT * FROM pageviews_table EMIT CHANGES;
+---------------+---------------+---------------+------------------+--------+---------+------+
| ROWTIME | WINDOWSTART | WINDOWEND | ROWKEY | USERID | PAGEID | TOTAL|
+---------------+---------------+---------------+------------------+--------+---------+------+
| 1557183919786 | 1557183900000 | 1557183960000 | User_5|+|Page_12 | User_5 | Page_12 | 1 |
| 1557183929488 | 1557183900000 | 1557183960000 | User_9|+|Page_39 | User_9 | Page_39 | 1 |
| 1557183930211 | 1557183900000 | 1557183960000 | User_1|+|Page_79 | User_1 | Page_79 | 1 |
| 1557183930687 | 1557183900000 | 1557183960000 | User_9|+|Page_34 | User_9 | Page_34 | 1 |
| 1557183929786 | 1557183900000 | 1557183960000 | User_5|+|Page_12 | User_5 | Page_12 | 2 |
| 1557183931095 | 1557183900000 | 1557183960000 | User_3|+|Page_43 | User_3 | Page_43 | 1 |
| 1557183930184 | 1557183900000 | 1557183960000 | User_1|+|Page_29 | User_1 | Page_29 | 1 |
| 1557183930727 | 1557183900000 | 1557183960000 | User_6|+|Page_93 | User_6 | Page_93 | 3 |
^CQuery terminated
当向表发出更改时,可以多次输出相同的键。这是因为每次表中的行发生更改时,都会发出该行。
使用SELECT
语句查找表中特定键的值。
SELECT * FROM pageviews_table WHERE ROWKEY='User_9|+|Page_39';
+------------------+---------------+---------------+---------------+--------+---------+-------+
| ROWKEY | WINDOWSTART | WINDOWEND | ROWTIME | USERID | PAGEID | TOTAL |
+------------------+---------------+---------------+---------------+--------+---------+-------+
| User_9|+|Page_39 | 1557183900000 | 1557183960000 | 1557183929488 | User_9 | Page_39 | 1 |
Query terminated
使用DROP TABLE
语句删除一张表。如果使用CREATE table
作为SELECT
来创建表,则必须首先终止相应的持久查询。
使用TERMINATE
语句来停止CTAS_USERS_FEMALE_0
查询:
TERMINATE CTAS_USERS_FEMALE_0;
Message
-------------------
Query terminated.
-------------------
使用DROP TABLE
语句删除users_female
表:
DROP TABLE users_female;
Message
-----------------------------------
Source USERS_FEMALE was dropped.
-----------------------------------
参考指南:
Develop ksqlDB Applications
Serialization
Quick Start using Community Components (Local)
KSQL语法表