FlinkCDC-Hudi:Mysql数据实时入湖全攻略四:两种FlinkSql kafka connector的特征与应用

FlinkCDC-Hudi:Mysql数据实时入湖全攻略四:两种FlinkSql kafka connector的特征与应用_第1张图片

前序:FlinkCDC-Hudi系列文章:

FlinkCDC-Hudi:Mysql数据实时入湖全攻略一:初试风云
FlinkCDC-Hudi:Mysql数据实时入湖全攻略二:Hudi与Spark整合时所遇异常与解决方案
FlinkCDC-Hudi:Mysql数据实时入湖全攻略三:探索实现FlinkCDC mysql 主从库同步高可用

一、背景

在生产实践中,通过FlinkCDC读取数据,除了落地hadoop入湖供下游离线使用外,也会存在写入kafka供实时程序消费使用。

那么flink里,kafka connector有哪些?各有什么特征?使用时要注意什么呢?且让我们开始flink kafka connector探索之旅。

二、测试环境准备

2.1 基础运行环境搭建

在开始实操探索之前,至少确保你已经搭建好了FlinkCDC-Hudi的运行环境。本文的测试环境基于FlinkCDC-Hudi:Mysql数据实时入湖全攻略一:初试风云。如果仅对flinkcdc写入kafka感兴趣,至少准备flink环境和flinkcdc依赖。

2.2 kafka sql connector环境搭建

测试flink sql写kafka,需要添加运行依赖flink-sql-connector-kafka。笔者使用的版本是flink-sql-connector-kafka_2.11-1.13.5.jar。


    org.apache.flink
    flink-sql-connector-kafka_2.11
    1.13.5
    provided


读者可以根据自己的运行环境下载对应的依赖包。maven依赖下载: flink-connector-kafka

2.3 Kafka 集群环境

笔者kafka使用kafka-2.7.0版本。读者如未配置kafka,可参见官方文档Kafka快速入门

2.4 mysql 环境准备

笔者在FlinkCDC-Hudi:Mysql数据实时入湖全攻略三:探索实现FlinkCDC mysql 主从库同步高可用 搭建了一主二从的Mysql环境,笔者的运行环境依赖使用这个环境。读者可以依此搭建。
读者如果使用自己的环境,需要确认mysql开启binlog并授以flinkcdc测试账号相应权限。

三、前置运行代码

本文相关测试在flink sql上运行。在搭建好上述环境后,执行以下后置代码,然后进入flink sql kafka connector测试环境。

3.1 mysql ddl

mysql> CREATE TABLE `test_1` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `data` varchar(10) DEFAULT NULL,
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

3.2 启动flink sql client

cd FLINK_HOME
./bin/yarn-session.sh -s 4 -jm 1024 -tm 2048 -nm flink-hudi -d 
./bin/sql-client.sh embedded -s yarn-session -j /home/zhangsirun/flink-1.13.5/lib/hudi-flink-bundle_2.11-0.11.0-SNAPSHOT.jar shell

3.3 设置Flink sql 运行变量

Flink SQL> set execution.checkpointing.interval=30sec;
Flink SQL> set pipeline.name = flinkcdc_test_1;

3.4 flink sql mysqlcdc ddl:

Flink SQL> create table mysql_test_1(
id bigint primary key not enforced,
data String,
create_time Timestamp(3)
) with (
'connector'='mysql-cdc',
'hostname'='192.168.2.101',
'port'='3306',
'server-id'='5800-5804',
'username'='user_test',
'password'='user_test_password',
'server-time-zone'='Asia/Shanghai',
'debezium.snapshot.mode'='initial',
'database-name'='flink_cdc',
'table-name'='test_1'
);  

四、FlinkSQL Kafka connector的两种实现

通过查阅Flink官方文档Connector/Table API Connectors,我们知道Flink kafka connector有两种实现:kafka和upsert kafka。这两种connector有什么特点呢?下面一一揭晓。

五、Kafka sql connector

Kafka sql connector是基础的kafka应用封装,用于生产/消费指写topic的数据。

5.1 元数据

这个connector提供了额外的元数据可用于表定义,topic,partittion,headers,leader-epoch,offset,timestamp,timestamp-type,这些都是与生产/消费相关的kafka基础信息。官方提供的应用样例:

CREATE TABLE KafkaTable (
  `event_time` TIMESTAMP(3) METADATA FROM 'timestamp',
  `partition` BIGINT METADATA VIRTUAL,
  `offset` BIGINT METADATA VIRTUAL,
  `user_id` BIGINT,
  `item_id` BIGINT,
  `behavior` STRING
) WITH (
  'connector' = 'kafka',
  'topic' = 'user_behavior',
  'properties.bootstrap.servers' = 'localhost:9092',
  'properties.group.id' = 'testGroup',
  'scan.startup.mode' = 'earliest-offset',
  'format' = 'csv'
);

5.2 配置与特征

使用这个connector时相关配置有很多,详情可见Flink/table/kafka connector。这里结合配置项做一些关键特征介绍。

5.2.1 connector类型

connector=kafka,必选 ,指定为使用kafka sql connector。

5.2.2 kafka相关基础配置

  • 必选项,properties.bootstrap.servers,指定集群
  • 必选项,topic/topic-pattern,二选一。topic-pattern可指定多个topic,用分号分隔。
  • 可选项,properties.group.id,消费组信息,不配置会按格式“KafkaSource-{tableIdentifier}”生成。
  • 更多的kafka原生配置通过 properties.*配置。如properties.security.protocol。

5.2.3 序列化相关

  • 必选项,format,value.format,二选一,指定消息体序列与反序列格式。
  • 可选项,key.format,指定消息key的序列化与反序列化格式。
  • 可选项,key.fields,指定主键字段,多个字段使用分号分隔。key.fields在生产消息时会使用key.format格式,根据分区函数发到相应分区。
  • 可选项,key.fields-prefix,如果key里的字段与value里的字段冲突时,可以配置Key前缀来解决冲突。

常用格式有csv,json,debezium-json,avro,raw。格式配置错误的话,会导致解析异常,进行导致作业失败。
FlinkCDC-Hudi:Mysql数据实时入湖全攻略四:两种FlinkSql kafka connector的特征与应用_第2张图片
更多格式与信息参看connetors/table/format

5.2.4 kafka sourse配置

scan.*,定义消费相关的配置。可以配置消费模式,起始offset,起始timestamp,分区发现时间间隔。

  • scan.startup.mode,消费启动模式。支持以下值配置:
说明
group-offsets 从zk/kafka上记录的消费者组offset开始消费,默认
earliest-offset 从最早的offset开始
latest-offset 从最新的offset开始
timestamp 每个分区从对应时间戳开始消费。时间戳通过scan.startup.timestamp-millis指定
specific-offsets 从用户指定的offset开始消费。offset通过配置scan.startup.specific-offsets指定,示例为:partition:0,offset:42;partition:1,offset:300
  • scan.topic-partition-discovery.interval,配置动态发现扫描时间间隔。定期扫描更新元数据,用于动态topic、动态分区发现。

5.2.5 kafka sink配置

sink.*,定义生产相关的配置。可配置key partitionner,生产一致性语义,生产并发。

  • sink.partitioner,分区函数。
  • sink.delivery-guarantee,消息传递一致性保证。支持以下值:
说明
none 不提供任何保证,可能丢数,可能重复
at-least-once 至少一次,保证不丢数,可能会重复
exactly-once 精确一次。使用kafka事务保证。下游消费者需要配置隔离等级。isolation.level (read_committed 或 read_uncommitted
  • sink.parallelism,生产并发度。默认使用与flink算子链相同的并发度。

5.3 Kafka sql connector应用

5.3.1 Kafka sql table ddl

Flink SQL> create table kafka_test_1(
  table_name String,
  id bigint primary key not enforced,
  data String,
  create_time Timestamp(3)
  ) with (
  'connector'='kafka',
  'topic'='test',
  'properties.bootstrap.servers' = 'broker:9092',
  'key.format'='json',
  'key.fields'='table_name;id',
  'value.format'='debezium-json'
  );

由于我们使用FlinkCDC mysql connector作为数据源,使用的value.format是debezium-json,同时定义了两个主键table_name;id,主键格式为json格式。

5.3.2 启动flink kafka sql connector作业

5.3.2.1 启动sink kafka作业

Flink SQL> set execution.checkpointing.interval=30sec;
Flink SQL> set pipeline.name = flinkcdc_test_1;
Flink SQL> insert into kafka_test_1 select * from mysql_test_1;

在flink session集群中顺利启动flink作业。
FlinkCDC-Hudi:Mysql数据实时入湖全攻略四:两种FlinkSql kafka connector的特征与应用_第3张图片

5.3.2.2 启动source kafka作业

在FlinkSql client中启动表查询:

Flink SQL> select * from kafka_test_1;

FlinkSql source kafka表视图:
FlinkCDC-Hudi:Mysql数据实时入湖全攻略四:两种FlinkSql kafka connector的特征与应用_第4张图片

5.3.3 Kafka消息验证

开启Kafka消费者,用于读取kafka消息,验证sink kafka。

KAFKA_HOME/bin/kafka-console-consumer.sh --bootstrap-server broker:9092 --topic test1  --property print.partition=true --property print.offset=true --property print.key=true  --from-beginning

5.3.3.1 insert语句验证

在mysql中插入数据:

mysql> insert into test_1 values(149,'data','2022-02-18 20:31:55');

消费到的数据:

Partition:3	Offset:11	{"table_name":"test_1","id":149}	{"before":null,"after":{"table_name":"test_1","id":149,"data":"d1","create_time":"2022-02-18 20:31:55"},"op":"c"}

消息按table_name,id以json格式生成key: {“table_name”:“test_1”,“id”:149}。
消息体为debezium-json格式。内容如下。其中

  • before字段为修改前内容
  • after有修改后的内容
  • op为操作类型,有两种值:c - create, d - delete。
{
	"before": null,
	"after": {
		"table_name": "test_1",
		"id": 149,
		"data": "d1",
		"create_time": "2022-02-18 20:1:55"
	},
	"op": "c"
}

对应flinkSql视图查询到一条新增数据:
FlinkCDC-Hudi:Mysql数据实时入湖全攻略四:两种FlinkSql kafka connector的特征与应用_第5张图片

5.3.3.2 update语句验证

通过mysql更新上述记录:

mysql> update test_1 set data='bigdata' where id=149;

一条mysyl update语句产生了两条kakfa消息。第一条代表delete旧值,旧值放在before字段。第二条代表创建新值,新值放在after字段。由于key相同,数据都发到了相同的分区。

Partition:3	Offset:12	{"table_name":"test_1","id":149}	{"before":{"table_name":"test_1","id":149,"data":"d1","create_time":"2022-02-18 20:31:55"},"after":null,"op":"d"}
Partition:3	Offset:13	{"table_name":"test_1","id":149}	{"before":null,"after":{"table_name":"test_1","id":149,"data":"bigdata","create_time":"2022-02-18 20:38:46"},"op":"c"}

对应FlinkSQL视图查询到update后的数据:
FlinkCDC-Hudi:Mysql数据实时入湖全攻略四:两种FlinkSql kafka connector的特征与应用_第6张图片

5.3.3.3 delete语句验证

在mysql中delete该记录:

mysql> delete from test_1 where id=149;

一条delete语句对应产生一条kafka消息。

Partition:3	Offset:14	{"table_name":"test_1","id":149}	{"before":{"table_name":"test_1","id":149,"data":"bigdata","create_time":"2022-02-18 20:38:46"},"after":null,"op":"d"}

FlinkSQL视图中数据被删除。
FlinkCDC-Hudi:Mysql数据实时入湖全攻略四:两种FlinkSql kafka connector的特征与应用_第7张图片

5.3.4 Kafka sql connector应用总结

行为 create update delete
source 读取c记录 读取到2条记录,合并为最新镜像 删除1条记录
sink 产生1条c记录 产生2条记录,c-d 产生1条d记录

六、Upsert Kafka connector

6.1 使用特点

Upsert Kafka connector 允许以 upsert 方式从 Kafka topic中读取或写入数据。

在upsert模式中,变量日志流中,所有的insert、update、delete事件都可以理解为update事件,任一事件发生时,对应记录的所有字段值都会更新为最新的值,delete视为将值更新为null。将数据写入kafka的时候,数据会按key进行分区,确保相同的key都会以相同的顺序进入到相同的分区。

6.2 配置

Upsert kafka connector配置与kafka connector的大致相同,两个关键的新增的配置如下:

  • sink.buffer-flush.max-rows,每次发送前最大的缓存记录数。缓存时,相同的key将会保留最新的记录。这样可以减少发送给kafka的数据,减少io shuffle。默认值为0,即不开启。配置时应与sink.buffer-flush.interval一起配置,配置值不可为负数。
  • sink.buffer-flush.interval,缓存刷新的时间间隔,超过配置的时间间隔后,将会发送一次数据。

6.3 Upsert kafka connector应用

6.3.1 Upsert kafka sql table ddl

  Flink SQL> create table upsert_kafka_test_2(
  table_name String,
  id bigint,
  data String,
  create_time Timestamp(3),
  PRIMARY KEY (`table_name`,`id`) NOT ENFORCED
  ) with (
  'connector'='upsert-kafka',
   'properties.bootstrap.servers' = 'broker:9092',
  'topic'='test2',
  'key.format'='json',
  'value.format'='json'
  );

6.3.2 启动Flink upsert kafka connector作业

6.3.2.1启动sink kafka作业

Flink SQL> set execution.checkpointing.interval=30sec;
Flink SQL> set pipeline.name = flinkcdc_upsert_kafka_test_1;
Flink SQL> insert into upsert_kafka_test_2 select * from mysql_test_1;

FlinkCDC-Hudi:Mysql数据实时入湖全攻略四:两种FlinkSql kafka connector的特征与应用_第8张图片

6.3.2.1 启动source kafka作业

Flink SQL>select * from upsert_kafka_test_2;

FlinkCDC-Hudi:Mysql数据实时入湖全攻略四:两种FlinkSql kafka connector的特征与应用_第9张图片

6.3.3 Kafka消息验证

6.3.3.1 insert语句验证

执行一条insert语句:

mysql> insert into test_1 values(151,'data','2022-02-21 10:31:55');

kafka consumer查看消息,收到的消息体就是按表ddl字段组织成的json。

Partition:1	Offset:152	{"table_name":"test_1","id":151}	{"table_name":"test_1","id":151,"data":"data","create_time":"2022-02-21 10:31:55"}

在FlinkSql client中直接新增数据:
FlinkCDC-Hudi:Mysql数据实时入湖全攻略四:两种FlinkSql kafka connector的特征与应用_第10张图片

6.3.3.2 update语句验证

在mysql中执行一条update:

mysql> update test_1 set data='bigdata' where id=151;

在kafka中收到一条更新数据,一条json里包含所有字段的最新值:

Partition:1	Offset:153	{"table_name":"test_1","id":151}	{"table_name":"test_1","id":151,"data":"bigdata","create_time":"2022-02-21 10:48:36"}

FlinkSQL client展示了更新后的数据:
FlinkCDC-Hudi:Mysql数据实时入湖全攻略四:两种FlinkSql kafka connector的特征与应用_第11张图片

6.3.3.3 delete语句验证

在mysql中执行一条delete语句:

mysql> delete from test_1 where id=151;

在kafka consumer中收到一条delete消息,消息的key为定义的主键,消息体为“null”。

Partition:1	Offset:154	{"table_name":"test_1","id":151}	null

在FlinkSQL client中对应的记录被删除:
FlinkCDC-Hudi:Mysql数据实时入湖全攻略四:两种FlinkSql kafka connector的特征与应用_第12张图片

6.3.4 Upsert kafka connector应用总结

Upsert kafka的增改时,消息体是表定义的ddl字段,以value.format定义的格式组织。删除时,消息体是null字符串。与Kafka connector不同的是,update只生产了一条记录。

行为 create update delete
source 读到key+数据json 读到key+数据json 读到key+null字符
sink 产生key+数据json 产生key+数据json 产生key+null字符

七、总结

至此,我们详细介绍了flink sql kafka的两个connector,对其配置、特征与应用。就我们观察到的现象而言,这两种connector适用于哪种场景吗?

  • Kafka sql connector提供常规的kafka生产与消费行为,适用于大部分应用场景。对于日志变更流,如果关心数据是如何变化的,可以选择Kafka sql connector。
  • Upsert Kafka sql connector提供的是upsert模式的生产与消费,适用于有数据更新合并,但只关心结果状态,不关心过程变化的应用。Upsert kafka开启buff后还能进一步减少数据量,减轻shuffle的压力。

至此,我们完成Flink kafka connector的学习,相信读者已经可以根据自己的业务场景灵活进行应用。下一节我们讲如何在FlinkSql中实现多路输出,敬请期待!

FlinkCDC-Hudi:Mysql数据实时入湖全攻略四:两种FlinkSql kafka connector的特征与应用_第13张图片

你可能感兴趣的:(Flink,Kafka,Hudi,kafka,mysql,spark)