这里定义了 Kafka 连接器对应的主题(topic),Kafka 服务器,消费者组 ID,消费者起始模式以及表格式。需要特别说明的是,在 KafkaTable 的字段中有一个 ts,它的声明中用到了METADATA FROM,这是表示一个“元数据列”(metadata column),它是由 Kafka 连接器的元数据“timestamp”生成的。这里的 timestamp 其实就是 Kafka 中数据自带的时间戳,我们把它直接作为元数据提取出来,转换成一个新的字段 ts。
CREATE TABLE KafkaTable (
`user` STRING,
`url` STRING,
`ts` TIMESTAMP(3) METADATA FROM 'timestamp'
) WITH (
'connector' = 'kafka',
'topic' = 'events',
'properties.bootstrap.servers' = 'localhost:9092',
'properties.group.id' = 'testGroup',
'scan.startup.mode' = 'earliest-offset',
'format' = 'csv'
)
正常情况下,Kafka 作为保持数据顺序的消息队列,读取和写入都应该是流式的数据,对应在表中就是仅追加(append-only)模式。如果我们想要将有更新操作(比如分组聚合)的结果表写入 Kafka,就一个“更新插入 Kafka”(Upsert Kafka)连接器。这个连接器支持以更新插入的方式向 Kafka 的 topic 中读写数据。具体来说,Upsert Kafka 连接器处理的是更新日志流。
如果作为TableSource,连接器会将读取到的 topic中的数据(key, value),解释为对当前 key 的数据值的更新,也就是查找动态表中 key 对应的一行数据,将 value 更新为最新的值;因为是 Upsert 操作,所以如果没有 key 对应的行,那么也会执行插入(INSERT)操作。另外,如果遇到 value 为空(null),连接器就把这条数据理解为对相应 key 那一行的删除(DELETE)操作。
如果作为 TableSink,Upsert Kafka 连接器会将有更新操作的结果表,转换成更新日志
(changelog)流。如果遇到插入(INSERT)或者更新后(UPDATE_AFTER)的数据,对应的是一个添加(add)消息,那么就直接正常写入 Kafka 主题;如果是删除(DELETE)或者更新前的数据,对应是一个撤回(retract)消息,那么就把 value 为空(null)的数据写入 Kafka。由于 Flink 是根据键(key)的值对数据进行分区的,这样就可以保证同一个 key 上的更新和删除消息都会落到同一个分区中。
CREATE TABLE pageviews_per_region (
user_region STRING,
pv BIGINT,
uv BIGINT,
PRIMARY KEY (user_region) NOT ENFORCED
) WITH (
'connector' = 'upsert-kafka',
'topic' = 'pageviews_per_region',
'properties.bootstrap.servers' = '...',
'key.format' = 'avro',
'value.format' = 'avro'
);
定义了一个 Upsert Kafka 表,它的字段中需要用PRIMARY KEY来指定主键,并且在WITH子句中分别指定key和value的序列化格式。
在JDBC连接器中,作为 TableSink 向数据库写入数据时,运行的模式取决于创建表的 DDL 是否定义了主键(primary key)。如果有主键,那么 JDBC 连接器就将以更新插入(Upsert)模式运行,可以向外部数据库发送按照指定键(key)的更新(UPDATE)和删除(DELETE)操作;如果没有定义主键,那么就将在追加(Append)模式下运行,不支持更新和删除操作。
CREATE TABLE MyTable (
id BIGINT,
name STRING,
age INT,
status BOOLEAN,
PRIMARY KEY (id) NOT ENFORCED
) WITH (
'connector' = 'jdbc',
'url' = 'jdbc:mysql://localhost:3306/mydatabase',
'table-name' = 'users'
);
Flink 提供的Elasticsearch的SQL连接器只能作为TableSink,将表数据写入Elasticsearch的索引(index)。Elasticsearch 连接器的使用与 JDBC 连接器非常相似,写入数据的模式同样由创建表的 DDL中是否有主键定义决定的。
CREATE TABLE MyTable (
user_id STRING,
user_name STRING
uv BIGINT,
pv BIGINT,
PRIMARY KEY (user_id) NOT ENFORCED
) WITH (
'connector' = 'elasticsearch-7',
'hosts' = 'http://localhost:9200',
'index' = 'users'
);
在流处理场景下,连接器作为 TableSink 向 HBase 写入数据时,采用的始终是更新插入
(Upsert)模式。也就是说,HBase 要求连接器必须通过定义的主键(primary key)来发送更新日志(changelog)。所以在创建表的 DDL 中,我们必须要定义行键(rowkey)字段,并将它声明为主键;如果没有用 PRIMARY KEY 子句声明主键,连接器会默认把 rowkey 作为主键。
由于 HBase 并不是关系型数据库,因此转换为 Flink SQL 中的表会稍有一些麻烦。在 DDL创建出的 HBase 表中,所有的列族(column family)都必须声明为 ROW 类型,在表中占据一个字段;而每个 family 中的列(column qualifier)则对应着 ROW 里的嵌套字段。我们不需要将 HBase 中所有的 family 和 qualifier 都在 Flink SQL 的表中声明出来,只要把那些在查询中用到的声明出来就可以了。
除了所有 ROW 类型的字段(对应着 HBase 中的 family),表中还应有一个原子类型的字段,它就会被识别为 HBase 的 rowkey。在表中这个字段可以任意取名,不一定非要叫 rowkey。
CREATE TABLE MyTable (
rowkey INT,
family1 ROW,
family2 ROW,
family3 ROW,
PRIMARY KEY (rowkey) NOT ENFORCED
) WITH (
'connector' = 'hbase-1.4',
'table-name' = 'mytable',
'zookeeper.quorum' = 'localhost:2181'
);
Flink 与 Hive 的集成比较特别。Flink 提供了“Hive 目录”(HiveCatalog)功能,允许使用
Hive 的“元存储”(Metastore)来管理 Flink 的元数据。这带来的好处体现在两个方面:
HiveCatalog 被设计为“开箱即用”,与现有的 Hive 配置完全兼容,我们不需要做任何的修改与调整就可以直接使用。注意只有 Blink 的计划器(planner)提供了 Hive 集成的支持,所以需要在使用 Flink SQL时选择Blink planner。
-- 设置 SQL 方言为 hive,创建 Hive 表
SET table.sql-dialect=hive;
CREATE TABLE hive_table (
user_id STRING,
order_amount DOUBLE
) PARTITIONED BY (dt STRING, hr STRING) STORED AS parquet TBLPROPERTIES (
'partition.time-extractor.timestamp-pattern'='$dt $hr:00:00',
'sink.partition-commit.trigger'='partition-time',
'sink.partition-commit.delay'='1 h',
'sink.partition-commit.policy.kind'='metastore,success-file'
);
-- 设置 SQL 方言为 default,创建 Kafka 表
SET table.sql-dialect=default;
CREATE TABLE kafka_table (
user_id STRING,
order_amount DOUBLE,
log_ts TIMESTAMP(3),
WATERMARK FOR log_ts AS log_ts - INTERVAL '5' SECOND – 定义水位线
) WITH (...);
-- 将 Kafka 中读取的数据经转换后写入 Hive
INSERT INTO TABLE hive_table
SELECT user_id, order_amount, DATE_FORMAT(log_ts, 'yyyy-MM-dd'),
DATE_FORMAT(log_ts, 'HH')
FROM kafka_table;
Hive内部提供了类SQL的查询语言,不过语法细节与标准SQL会有一些出入,相当于是 SQL 的一种“方言”(dialect)。为了提高与 Hive 集成时的兼容性,Flink SQL 提供了一个非常有趣而强大的功能:可以使用方言来编写 SQL 语句。换句话说,我们可以直接在 Flink中写 Hive SQL 来操作 Hive 表,这无疑给我们的读写处理带来了极大的方便。
create table myTable(
stt string,
edt string,
province_id bigint,
province_name string,
area_code string,
iso_code string,
iso_3166_2 string,
order_amount decimal(20,2),
order_count bigint,
ts bigint,
primary key(stt,edt,province_id) not enforced
)with(
'connector' = 'clickhouse',
'url' = 'clickhouse://hadoop162:8123',
'database-name' = 'mydb',
'table-name' = 'myTable',
'sink.batch-size' = '100',
'sink.flush-interval' = '1000',
'sink.max-retries' = '3'
));