本文档内容基于flink-1.16.x
,其他版本的整理,请查看本人博客的 flink 专栏其他文章。
Flink提供了一组可以与表连接器一起使用的表格式。表格式是一种存储格式,定义如何将二进制数据映射到表字段。
Flink支持以下格式:
格式 | 连接器 |
---|---|
CSV | Apache Kafka, Upsert Kafka, Amazon Kinesis Data Streams, Filesystem |
JSON | Apache Kafka, Upsert Kafka, Amazon Kinesis Data Streams, Filesystem, Elasticsearch |
Apache Avro | Apache Kafka, Upsert Kafka, Amazon Kinesis Data Streams, Filesystem |
Confluent Avro | Apache Kafka, Upsert Kafka |
Debezium CDC | Apache Kafka, Filesystem |
Canal CDC | Apache Kafka, Filesystem |
Maxwell CDC | Apache Kafka, Filesystem |
OGG CDC | Apache Kafka, Filesystem |
Apache Parquet | Filesystem |
Apache ORC | Filesystem |
Raw | Apache Kafka, Upsert Kafka, Amazon Kinesis Data Streams, Filesystem |
支持:
CSV格式允许基于CSV schema读写CSV格式的数据。目前,CSV schema来源于表schema定义。
为了使用CSV格式,以下依赖项对于使用自动化构建工具(如Maven或SBT)的项目和带有SQL JAR包的SQL Client都是必需的。
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-csvartifactId>
<version>1.16.0version>
dependency>
注意自己使用的 flink 版本。
CREATE TABLE user_behavior (
user_id BIGINT,
item_id BIGINT,
category_id BIGINT,
behavior STRING,
ts TIMESTAMP(3)
) WITH (
'connector' = 'kafka',
'topic' = 'user_behavior',
'properties.bootstrap.servers' = 'localhost:9092',
'properties.group.id' = 'testGroup',
'format' = 'csv',
'csv.ignore-parse-errors' = 'true',
'csv.allow-comments' = 'true'
)
选项 | 要求 | 可被转发 | 默认值 | 类型 | 描述 |
---|---|---|---|---|---|
format | 必选 | 否 | (none) | String | 指定使用哪种格式,这儿应该是 csv 。 |
csv.field-delimiter | 可选 | 是 | , | String | 字段值分隔符号(默认为英文逗号**,),必须是单个字符。 可以使用反斜杠来指定特殊字符,比如\t**代表制表符。 也可以在纯SQL中使用unicode编码来指定,比如:csv.field-delimiter’ = U&'\0001,表示 0x01 字符。 |
csv.disable-quote-character | 可选 | 是 | false | Boolean | 禁用用于封闭字段值的引号符号(默认为false)。如果为true,必须设置csv.quote-character选项。 |
csv.quote-character | 可选 | 是 | " | String | 封闭字段值的引号符号(默认为英文双引号 " )。 |
csv.allow-comments | 可选 | 是 | false | Boolean | 忽略以 # 开头的注释行(默认禁用)。如果启动用,确认同时忽略转换错误,以允许出现空行数据。 |
csv.ignore-parse-errors | 可选 | 否 | false | Boolean | 跳过转换错误的属性和数据行,而不是失败。如果出现错误,字段值将设置为null。 |
csv.array-element-delimiter | 可选 | 是 | ; | String | 数组元素分隔符号(默认为英文分号 ; )。 |
csv.escape-character | 可选 | 是 | (none) | String | 用于转义字段值的转移符号(默认禁用)。 |
csv.null-literal | 可选 | 是 | (none) | String | 将null字符串作为NULL赋给对应字段值(默认禁用)。 |
csv.write-bigdecimal-in-scientific-notation | 可选 | 是 | true | Boolean | 允许使用科学计数法表示 BigDecimal 数据类型(默认开启)。比如,10000 将会默认被表示为 1E+5 ,如果设置该选项为 false ,将会使用 10000 表示。注:只有当值不为 0 ,并且为 10 的倍数时,才会被转化为科学计数法。 |
目前,CSV schema总是派生于表schema。目前还不支持直接显式定义CSV schema。
Flink CSV格式使用jackson databind API
解析和生成CSV字符串。
Flink类型到CSV类型的类型映射如下表所示。
Flink SQL type | CSV type |
---|---|
CHAR / VARCHAR / STRING | string |
BOOLEAN | boolean |
BINARY / VARBINARY | base64 编码的字符串 |
DECIMAL | number |
TINYINT | number |
SMALLINT | number |
INT | number |
BIGINT | number |
FLOAT | number |
DOUBLE | number |
DATE | date 格式的字符串 |
TIME | time 格式的字符串 |
TIMESTAMP | date-time 格式的字符串 |
INTERVAL | number |
ARRAY | array |
ROW | object |
支持:
JSON格式允许基于JSON schema读写JSON格式的数据。目前,JSON schema派生于表schema。
JSON format 支持仅追加流,除非是你使用的连接器明确支持 retract流
和/或 upsert流
,比如 Upsert Kafka
连接器。
如果你需要将数据写入 retract流
和/或 upsert流
,建议你使用 CDC format,比如 Debezium JSON
和 Cannal JSON
。
为了使用Json格式,以下依赖项对于使用自动化构建工具(如Maven或SBT)的项目和带有SQL JAR包的SQL Client都是必需的。
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-jsonartifactId>
<version>1.16.0version>
dependency>
注意自己使用的 flink 版本。
下面是一个使用Kafka连接器和JSON格式创建表的示例。
CREATE TABLE user_behavior (
user_id BIGINT,
item_id BIGINT,
category_id BIGINT,
behavior STRING,
ts TIMESTAMP(3)
) WITH (
'connector' = 'kafka',
'topic' = 'user_behavior',
'properties.bootstrap.servers' = 'localhost:9092',
'properties.group.id' = 'testGroup',
'format' = 'json',
'json.fail-on-missing-field' = 'false',
'json.ignore-parse-errors' = 'true'
)
选项 | 要求 | 可被转发 | 默认值 | 类型 | 描述 |
---|---|---|---|---|---|
format | 必选 | 否 | (none) | String | 指定使用哪种格式,这儿必须是 json 。 |
json.fail-on-missing-field | 可选 | 否 | false | Boolean | 如果丢失了schema中指定的属性,是否发生失败。 |
json.ignore-parse-errors | 可选 | 否 | false | Boolean | 如果转化错误,直接跳过该属性和行,而不是发生失败。该类型的错误,属性会被设置为null。 |
json.timestamp-format.standard | 可选 | 是 | SQL | String | 声明输入和输出的时间戳格式。当前支持的格式为SQL 以及 ISO-8601。 可选参数 SQL 将会以 yyyy-MM-dd HH:mm:ss.s{precision} 的格式解析时间戳, 例如 2020-12-30 12:13:14.123 ,且会以相同的格式输出。 可选参数 ISO-8601 将会以 yyyy-MM-ddTHH:mm:ss.s{precision} 的格式解析输入时间戳, 例如 2020-12-30T12:13:14.123 ,且会以相同的格式输出。 |
json.map-null-key.mode | 可选 | 是 | FAIL | String | 指定匹配数据时序列化键为null的处理模式。目前支持:FAIL、DROP、LITERAL。 FAIL:遇到null键匹配时抛出异常。 DROP:遇到null键匹配时丢弃数据。 LITERAL:替换null键为字符串字面量。字符串字面量通过 json.map-null-key.literal 选项定义。 |
json.map-null-key.literal | 可选 | 是 | null | String | 当设置 json.map-null-key.mode 选项为 LITERAL 时,指定代替null键的字符串字面量。 如果设置为 null ,则表的schema字段名null就会和实际JSON数据中的 null 键进行匹配; 如果设置为 null-key ,则表的schema字段名null-key就会和实际JSON数据中的 null 键进行匹配。 |
json.encode.decimal-as-plain-number | 可选 | 是 | false | Boolean | 编码所有的数字为普通数字而不是科学计数法数字。 默认情况改下,数据可能会使用科学计数法,比如: 0.000000027 会被默认编码为2.7E-8 。如果设置这个选项为true ,则会编码为0.000000027 。 |
目前,JSON schema总是派生于表schema。目前还不支持直接显式定义JSON schema。
Flink JSON格式使用jackson databind API
解析和生成JSON字符串。
下表列出了从Flink类型到JSON类型的类型映射。
Flink SQL type | JSON type |
---|---|
CHAR / VARCHAR / STRING | string |
BOOLEAN | boolean |
BINARY / VARBINARY | base64 编码的字符串 |
DECIMAL | number |
TINYINT | number |
SMALLINT | number |
INT | number |
BIGINT | number |
FLOAT | number |
DOUBLE | number |
DATE | date 格式的字符串 |
TIME | time 格式的字符串 |
TIMESTAMP | date-time 格式的字符串 |
TIMESTAMP_WITH_LOCAL_TIME_ZONE | date-time 格式的字符串,使用 UTC 时区 |
INTERVAL | number |
ARRAY | array |
MAP / MULTISET | object |
ROW | object |
支持:
Apache Avro格式允许基于Avro schema读写Avro格式的数据。目前,Avro schema派生于表schema。
为了使用Avro格式,以下依赖项对于使用自动化构建工具(如Maven或SBT)的项目和带有SQL JAR包的SQL Client都是必需的。
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-avroartifactId>
<version>1.16.0version>
dependency>
注意自己所使用的 flink 的版本。
下面是一个使用Kafka连接器和Avro格式创建表的例子。
CREATE TABLE user_behavior (
user_id BIGINT,
item_id BIGINT,
category_id BIGINT,
behavior STRING,
ts TIMESTAMP(3)
) WITH (
'connector' = 'kafka',
'topic' = 'user_behavior',
'properties.bootstrap.servers' = 'localhost:9092',
'properties.group.id' = 'testGroup',
'format' = 'avro'
)
选项 | 要求 | 可被转发 | 默认值 | 类型 | 描述 |
---|---|---|---|---|---|
format | 必选 | 否 | (none) | String | 指定使用哪种格式,这儿应该是 avro 。 |
avro.codec | 可选 | 是 | (none) | String | 只用于Filesystem 文件系统,指定avro的压缩格式。默认没有压缩。可用的枚举有:deflate、snappy、bzip2、xz。 |
目前,Avro schema总是派生于表schema。目前还不支持直接显式定义Avro schema。下表列出了从Flink类型到Avro类型的类型映射。
Flink SQL 类型 | Avro 类型 | Avro 逻辑类型 |
---|---|---|
CHAR / VARCHAR / STRING | string | |
BOOLEAN | boolean | |
BINARY / VARBINARY | bytes | |
DECIMAL | fixed | decimal |
TINYINT | int | |
SMALLINT | int | |
INT | int | |
BIGINT | long | |
FLOAT | float | |
DOUBLE | double | |
DATE | int | date |
TIME | int | time-millis |
TIMESTAMP | long | timestamp-millis |
ARRAY | array | |
MAP (key 必须是 string/char/varchar 类型) |
map | |
MULTISET (元素必须是 string/char/varchar 类型) |
map | |
ROW | record |
除了上面列出的类型外,Flink还支持读写可空类型。Flink将可为空的类型映射到Avro联合(某值,null),其中某值是从Flink类型转换而来的Avro类型。
有关Avro类型的更多信息,可以参考Avro规范。
支持:
Avro Schema Registry(avro-confluent)格式允许你读取被io.confluent.kafka.serializers.KafkaAvroSerializer
序列化的记录, 并写入可以被io.confluent.kafka.serializers.KafkaAvroDeserializer
反序列化读取的记录。
当读取(反序列化)这种格式的数据时,根据数据中的schema版本id从配置的Confluent schema Registry
中获取Avro写入schema,同时从表schema推断读取schema。
当用这种格式写入(序列化)一条数据时,Avro schema将从表schema推断出用于检索的schema id,主要通过avro-confluent.subject配置的主题名进行查找。
Avro Schema Registry格式只能与Apache Kafka SQL
连接器或Upsert Kafka SQL
连接器结合使用。
为了使用Avro Schema Registry格式,以下依赖项对于使用自动化构建工具(如Maven或SBT)的项目和带有SQL JAR包的SQL Client都是必需的。
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-avro-confluent-registryartifactId>
<version>1.16.0version>
dependency>
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-avroartifactId>
<version>1.16.0version>
dependency>
注意自己使用的 flink 版本。
使用原始UTF-8字符串作为Kafka键以及在Schema Registry中注册的Avro记录作为Kafka值注册的表:
CREATE TABLE user_created (
-- -- 一个映射到kafka原生UTF-8字符串key的字段
the_kafka_key STRING,
-- 一些Avro属性字段作为kafka value
id STRING,
name STRING,
email STRING
) WITH (
'connector' = 'kafka',
'topic' = 'user_events_example1',
'properties.bootstrap.servers' = 'localhost:9092',
-- UTF-8字符串作为kafka key,使用“the_kafka_key”表字段
'key.format' = 'raw',
'key.fields' = 'the_kafka_key',
'value.format' = 'avro-confluent',
'value.avro-confluent.url' = 'http://localhost:8082',
'value.fields-include' = 'EXCEPT_KEY'
)
我们可以如下方式将数据写入kafka表:
INSERT INTO user_created
SELECT
-- 赋值user id字段值作为kafka key
id as the_kafka_key,
-- 所有字段值
id, name, email
FROM some_table
Kafka键和值都在Schema Registry中注册为Avro record:
CREATE TABLE user_created (
-- 一个映射到“id” avro属性字段作为kafka key
kafka_key_id STRING,
-- 一些映射到avro属性字段作为kafka value
id STRING,
name STRING,
email STRING
) WITH (
'connector' = 'kafka',
'topic' = 'user_events_example2',
'properties.bootstrap.servers' = 'localhost:9092',
-- 注意:由于哈希分区的存在,Kafka key上下文中的schema演化几乎不可能向后或向前兼容。
'key.format' = 'avro-confluent',
'key.avro-confluent.url' = 'http://localhost:8082',
'key.fields' = 'kafka_key_id',
-- 在这个例子中,我们希望Kafka key和value的Avro类型都包含字段'id' => 在与Kafka key字段相关联的表字段名前添加一个前缀,以避免冲突
'key.fields-prefix' = 'kafka_key_',
'value.format' = 'avro-confluent',
'value.avro-confluent.url' = 'http://localhost:8082',
'value.fields-include' = 'EXCEPT_KEY',
-- 从flink 1.13版本开始,subject有默认值,尽管可以被覆盖
'key.avro-confluent.subject' = 'user_events_example2-key2',
'value.avro-confluent.subject' = 'user_events_example2-value2'
)
使用upsert-kafka连接器的表示例,其中Kafka value在Schema Registry中注册为Avro记录:
CREATE TABLE user_created (
-- 一个映射到kafka原生UTF-8字符串key的字段
kafka_key_id STRING,
-- 一些映射到avro属性的字段作为kafka value
id STRING,
name STRING,
email STRING,
-- upsert-kafka连接器要求有一个主键来定义upsert行为
PRIMARY KEY (kafka_key_id) NOT ENFORCED
) WITH (
'connector' = 'upsert-kafka',
'topic' = 'user_events_example3',
'properties.bootstrap.servers' = 'localhost:9092',
-- UTF-8字符串作为kafka key
-- 在这个案例中不指定'key.fields',因为它由表的主键指定
'key.format' = 'raw',
-- In this example, we want the Avro types of both the Kafka key and value to contain the field 'id'
-- 在这个例子中,我们希望Kafka key和value的Avro类型都包含字段'id' => 在与Kafka key字段相关联的表字段名前添加一个前缀,以避免冲突
'key.fields-prefix' = 'kafka_key_',
'value.format' = 'avro-confluent',
'value.avro-confluent.url' = 'http://localhost:8082',
'value.fields-include' = 'EXCEPT_KEY'
)
选项 | 要求 | 可被转发 | 默认值 | 类型 | 描述 |
---|---|---|---|---|---|
format | 必选 | 否 | (none) | String | 指定使用哪种模式,这儿应该是 avro-confluent |
avro-confluent.basic-auth.credentials-source | 可选 | 是 | (none) | String | schema注册的基础认证证书资 |
avro-confluent.basic-auth.user-info | 可选 | 是 | (none) | String | schema注册的基础认证用户信息 |
avro-confluent.bearer-auth.credentials-source | 可选 | 是 | (none) | String | schema注册的持有者认证证书源 |
avro-confluent.bearer-auth.token | 可选 | 是 | (none) | String | schema注册的持有者认证令牌 token 源 |
avro-confluent.properties | 可选 | 是 | (node) | Map | 转发到下面 schema 注册的属性 map 表,这对于没有通过Flink配置选项正式公开的选项很有用,但是 Flink 选项拥有更高的优先级。 |
avro-confluent.ssl.keystore.location | 可选 | 是 | (none) | String | SSL秘钥库文件存储位置 |
avro-confluent.ssl.keystore.password | 可选 | 是 | (none) | String | SSL秘钥库密码 |
avro-confluent.ssl.truststore.location | 可选 | 是 | (none) | String | SSL truststore的文件存储位置 |
avro-confluent.ssl.truststore.password | 可选 | 是 | (none) | String | SSL truststore的密码 |
avro-confluent.subject | 可选 | 是 | (none) | String | Confluent模式注册中心主题,在该主题下注册此格式在序列化期间使用的schema。默认情况下,kafka 和 upsert-kafka 连接器使用 或 作为默认主题名。但对于其他连接器(例如: filesystem ),当用作接收器时,subject选项是必需的。 |
vro-confluent.url | 必选 | 是 | (none) | String | 用于获取/注册Confluent Schema Registry schema的URL |
目前,Apache Flink总是使用表schema在反序列化期间派生Avro读取schema,在序列化期间派生Avro写入schema。
目前还不支持直接显式定义Avro模式。 Avro和Flink数据类型之间的映射请参见Apache Avro Format。
除了上面列出的类型外,Flink还支持读写可空类型。Flink将可为空的类型映射到Avro联合(某值,null),其中某值是从Flink类型转换而来的Avro类型。
有关Avro类型的更多信息,可以参考Avro规范。
Protocol Buffers Protobuf format 允许用户读写 Protobuf 数据,基于 Protobuf 子类。
为了使用 Protobuf 格式,以下依赖项对于使用自动化构建工具(如Maven或SBT)的项目和带有SQL JAR包的SQL Client都是必需的
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-protobufartifactId>
<version>1.16.0version>
dependency>
下面是使用 kafka 连接器和 Protobuf 格式进行建表的案例。
下面是 proto 定义文件
syntax = "proto2";
package com.example;
option java_package = "com.example";
option java_multiple_files = true;
message SimpleTest {
optional int64 uid = 1;
optional string name = 2;
optional int32 category_type = 3;
optional bytes content = 4;
optional double price = 5;
map value_map = 6;
repeated InnerMessageTest value_arr = 7;
optional Corpus corpus_int = 8;
optional Corpus corpus_str = 9;
message InnerMessageTest{
optional int64 v1 =1;
optional int32 v2 =2;
}
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 7;
}
}
-j
参数。下面是具体的建表语句
CREATE TABLE simple_test (
uid BIGINT,
name STRING,
category_type INT,
content BINARY,
price DOUBLE,
value_map map<BIGINT, row<v1 BIGINT, v2 INT>>,
value_arr array<row<v1 BIGINT, v2 INT>>,
corpus_int INT,
corpus_str STRING
) WITH (
'connector' = 'kafka',
'topic' = 'user_behavior',
'properties.bootstrap.servers' = 'localhost:9092',
'properties.group.id' = 'testGroup',
'format' = 'protobuf',
'protobuf.message-class-name' = 'com.example.SimpleTest',
'protobuf.ignore-parse-errors' = 'true'
)
参数 | 要求 | 可被转化 | 默认值 | 类型 | 描述 |
---|---|---|---|---|---|
format | 必选 | 否 | (none) | String | 指定要使用的 format ,这儿必须为:protobuf |
protobuf.message-class-name | required | no | (none) | String | Protobuf 子类的完全限定名。该名字必须匹配 proto 定义文件中的 message 名。 $ 支持内部类名,比如:com.exmample.OuterClass$MessageClass |
protobuf.ignore-parse-errors | optional | no | false | Boolean | 是否跳过转化失败的行,而不是让运行失败。 |
protobuf.read-default-values | optional | yes | false | Boolean | 只有生成的子类版本为 proto2 时,该参数才其作用。如果将该参数设置为 true,proto 文件中定义的 format 格式将会读取空值为默认值。如果该参数设置为 false,在 binary protobuf message 中不存在的数据元素,format 格式将会生成 null 值。如果 proto 语法为 proto3,该参数将会被强制设置为 true,因为 proto3 的标准就是使用默认值。 |
protobuf.write-null-string-literal | optional | no | “” | String | 当序列化 protobuf 数据时,该参数用来指定 Protobuf 中 array/map 中的 null 值的字符串字面量。 |
下面表格列出了 flink 类型和 protobuf 类型之间的类型映射。
Flink SQL type | Protobuf type | Description |
---|---|---|
CHAR / VARCHAR / STRING | string | |
BOOLEAN | bool | |
BINARY / VARBINAR | bytes | |
INT | int32 | |
BIGINT | int64 | |
FLOAT | float | |
DOUBLE | double | |
ARRAY | repeated | 元素可以为 null,默认字符串的值可以通过 write-null-string-literal 指定。 |
MAP | map | key 和 value 都不能为 null,默认字符串的值可以通过 write-null-string-literal 指定。 |
ROW | message | |
VARCHAR / CHAR / TINYINT / SMALLINT / INTEGER / BIGINT | enum | protobuf 的枚举值可以被映射为 flink 相对行的字符串或数字。 |
由于 protobuf 不允许在 map 和 array 中出现 null 值,因此我们需要在将 flink rows 转化为 protobuf 时,自动生成默认值。
Protobuf Data Type | Default Value |
---|---|
int32 / int64 / float / double | 0 |
string | “” |
bool | false |
enum | 枚举中的第一个元素 |
binary | ByteString.EMPTY |
message | MESSAGE.getDefaultInstance() |
在序列化过程中,无法保证同一个 one-of 组内的 flink 的属性只包含最多一个合法值。在序列化时,每个属性将会根据 flink schema 中的属性顺序进行设置,因此在相同的 one-of 组中,上面的属性将会覆盖下面的属性。
可以参考 Language Guide (proto2) 或 Language Guide (proto3) 来获取更多有关 protobuf 类型的信息。
支持:
Debezium 是一个 CDC(Changelog Data Capture,变更数据捕获)工具,可以把来自 MySQL
、PostgreSQL
、Oracle
、Microsoft SQL Server
和许多其他数据库的更改实时流传输到 Kafka 中。
Debezium 为变更日志提供了统一的格式结构,并支持使用 JSON
和 Apache Avro
序列化消息。
Flink 支持将 Debezium JSON
和 Avro
消息解析为 INSERT / UPDATE / DELETE
消息到 Flink SQL 系统中。在很多情况下,这个特性非常有用,例如
Flink 还支持将 Flink SQL 中的 INSERT / UPDATE / DELETE
消息编码为 Debezium
格式的 JSON
或 Avro
消息,输出到 Kafka 等存储中。
但需要注意的是,目前 Flink 还不支持将 UPDATE_BEFORE
和 UPDATE_AFTER
合并为一条 UPDATE
消息。
因此,Flink 将 UPDATE_BEFORE
和 UPDATE_AFTER
分别编码为 DELETE
和 INSERT
类型的 Debezium
消息。
Debezium Avro
为了使用Debezium格式,以下依赖项对于使用自动化构建工具(如Maven或SBT)的项目和带有SQL JAR包的SQL Client都是必需的
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-avro-confluent-registryartifactId>
<version>1.16.0version>
dependency>
Debezium Json
为了使用Debezium格式,以下依赖项对于使用自动化构建工具(如Maven或SBT)的项目和带有SQL JAR包的SQL Client都是必需的
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-jsonartifactId>
<version>1.16.0version>
dependency>
注意自己使用的 flink 版本。
注意: 请参考 Debezium 文档,
了解如何设置 Debezium Kafka Connect 用来将变更日志同步到 Kafka 主题。
Debezium 为变更日志提供了统一的格式,下面是一个 JSON 格式的从 MySQL product 表捕获的更新操作的简单示例:
{
"before": {
"id": 111,
"name": "scooter",
"description": "Big 2-wheel scooter",
"weight": 5.18
},
"after": {
"id": 111,
"name": "scooter",
"description": "Big 2-wheel scooter",
"weight": 5.15
},
"source": {
...
},
"op": "u",
"ts_ms": 1589362330904,
"transaction": null
}
注意: 参考 Debezium 文档,了解每个字段的含义。
MySQL product表有4列(id、name、description、weight)。上面的 JSON 消息是 products 表上的一条更新事件,
其中 id = 111 的行的 weight 值从 5.18 更改为 5.15。假设此消息已同步到 Kafka 主题 products_binlog 中,则可以使用以下 DDL 来读取此主题并解析更改事件。
CREATE TABLE topic_products (
-- schema 与 MySQL 的 products 表完全相同
id BIGINT,
name STRING,
description STRING,
weight DECIMAL(10, 2)
) WITH (
'connector' = 'kafka',
'topic' = 'products_binlog',
'properties.bootstrap.servers' = 'localhost:9092',
'properties.group.id' = 'testGroup',
-- 使用 'debezium-json' format 来解析 Debezium 的 JSON 消息
-- 如果 Debezium 用 Avro 编码消息,请使用 'debezium-avro-confluent'
'format' = 'debezium-json' -- 如果 Debezium 用 Avro 编码消息,请使用 'debezium-avro-confluent'
)
在某些情况下,用户在设置 Debezium Kafka Connect 时,可能会开启 Kafka 的配置 value.converter.schemas.enable ,用来在消息体中包含 schema 信息。
然后,Debezium JSON 消息可能如下所示:
{
"schema": {
...
},
"payload": {
"before": {
"id": 111,
"name": "scooter",
"description": "Big 2-wheel scooter",
"weight": 5.18
},
"after": {
"id": 111,
"name": "scooter",
"description": "Big 2-wheel scooter",
"weight": 5.15
},
"source": {
...
},
"op": "u",
"ts_ms": 1589362330904,
"transaction": null
}
}
为了解析这类消息,需要在上述 DDL WITH 子句中添加选项 ‘debezium-json.schema-include’ = ‘true’(默认为 false)。
建议不要包含 schema 的描述,因为这样会使消息变得非常冗长,并降低解析性能。
在将主题注册为 Flink 表之后,可以将 Debezium 消息用作变更日志源。
-- MySQL "products" 的实时物化视图
-- 计算相同产品的最新平均重量
SELECT name, AVG(weight) FROM topic_products GROUP BY name;
-- 将 MySQL "products" 表的所有数据和增量更改同步到
-- Elasticsearch "products" 索引,供将来查找
INSERT INTO elasticsearch_products
SELECT * FROM topic_products;
以下format元数据可以在表定义中作为只读虚拟(VIRTUAL)列。
注意:只有在对应的连接器可以传递 format 元数据时,format 元数据属性才可用。目前,只有 kafka 连接器可以暴露元数据属性到他的 value format。
键 | 数据类型 | 描述 |
---|---|---|
schema | STRING NULL | payload中JSON格式的schema。如果Debezium数据中不包含schema,则返回NULL。 |
ingestion-timestamp | TIMESTAMP_LTZ(3) NULL | 连接器处理时间的时间戳。和Debezium数据中的ts_ms 属性一致。 |
source.timestamp | TIMESTAMP_LTZ(3) NULL | source系统创建事件的时间戳。和Debezium数据中的source.ts_ms 属性一致。 |
source.database | STRING NULL | 原始数据库名称。和Debezium数据中的source.db 属性一致。 |
source.schema | STRING NULL | 原始数据库的schema。和Debezium数据中的source.schema 属性一致。 |
source.table | STRING NULL | 原始数据库表名。和Debezium数据中的 source.collection 属性一致。 |
source.properties | MAP |
source源的属性表。和Debezium数据中的source 属性一致。 |
下面的例子展示了如何在Kafka中访问Debezium元数据字段:
CREATE TABLE KafkaTable (
origin_ts TIMESTAMP(3) METADATA FROM 'value.ingestion-timestamp' VIRTUAL,
event_time TIMESTAMP(3) METADATA FROM 'value.source.timestamp' VIRTUAL,
origin_database STRING METADATA FROM 'value.source.database' VIRTUAL,
origin_schema STRING METADATA FROM 'value.source.schema' VIRTUAL,
origin_table STRING METADATA FROM 'value.source.table' VIRTUAL,
origin_properties MAP<STRING, STRING> METADATA FROM 'value.source.properties' 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',
'value.format' = 'debezium-json'
);
Flink 提供了 debezium-avro-confluent
和 debezium-json
两种 format 来解析 Debezium 生成的 JSON 格式和 Avro 格式的消息。
请使用 debezium-avro-confluent
来解析 Debezium 的 Avro
消息,使用 debezium-json
来解析 Debezium 的 JSON
消息。
Debezium Avro
参数 | 必选 | 默认值 | 类型 | 描述 |
---|---|---|---|---|
format | 必选 | (none) | String | 指定使用哪个format,这儿应该是 debezium-avro-confluent 。 |
debezium-avro-confluent.basic-auth.credentials-source | 可选 | (none) | String | Basic auth credentials source for Schema Registry |
debezium-avro-confluent.basic-auth.user-info | 可选 | (none) | String | Basic auth user info for schema registry |
debezium-avro-confluent.bearer-auth.credentials-source | 可选 | (none) | String | Bearer auth credentials source for Schema Registry |
debezium-avro-confluent.bearer-auth.token | 可选 | (none) | String | Bearer auth token for Schema Registry |
debezium-avro-confluent.properties | 可选 | (none) | Map | 转发到下面 schema 注册的属性 map 表,这对于没有通过Flink配置选项正式公开的选项很有用,但是 Flink 选项拥有更高的优先级。 |
debezium-avro-confluent.ssl.keystore.location | 可选 | (none) | String | Location / File of SSL keystore |
debezium-avro-confluent.ssl.keystore.password | 可选 | (none) | String | Password for SSL keystore |
debezium-avro-confluent.ssl.truststore.location | 可选 | (none) | String | Location / File of SSL truststore |
debezium-avro-confluent.ssl.truststore.password | 可选 | (none) | String | Password for SSL truststore |
debezium-avro-confluent.subject | 可选 | (none) | String | Confluent模式注册中心主题,在该主题下注册此格式在序列化期间使用的schema。 默认情况下, kafka 和upsert-kafka 连接器使用 或 作为默认主题名。但对于其他连接器(例如:filesystem ),当用作接收器时,subject选项是必需的。 |
debezium-avro-confluent.url | 必选 | (none) | String | Confluent Schema Registry 获取/注册 schema 的URL. |
Debezium Json
参数 | 是否必选 | 默认值 | 类型 | 描述 |
---|---|---|---|---|
format | 必选 | (none) | String | 指定要使用的格式,此处应为 debezium-json 。 |
debezium-json.schema-include | 可选 | false | Boolean | 设置 Debezium Kafka Connect 时,用户可以启用 Kafka 配置 value.converter.schemas.enable 以在消息中包含 schema。此选项表明 Debezium JSON 消息是否包含 schema。 |
debezium-json.ignore-parse-errors | 可选 | false | Boolean | 当解析异常时,是跳过当前字段或行,还是抛出错误失败(默认为false,即抛出错误失败)。如果忽略字段的解析异常,则会将该字段值设置为null。 |
debezium-json.timestamp-format.standard | 可选 | SQL | String | 声明输入和输出的时间戳格式。当前支持的格式为 SQL 以及 ‘ISO-8601’。 可选参数 SQL 将会以 yyyy-MM-dd HH:mm:ss.s{precision} 的格式解析时间戳, 例如 ‘2020-12-30 12:13:14.123’,且会以相同的格式输出。 可选参数 ISO-8601 将会以 yyyy-MM-ddTHH:mm:ss.s{precision} 的格式解析输入时间戳, 例如 ‘2020-12-30T12:13:14.123’ ,且会以相同的格式输出。 |
debezium-json.map-null-key.mode | 选填 | FAIL | String | 指定处理 Map 中 key 值为空的方法. 当前支持的值有 FAIL , DROP 和 LITERAL 。 FAIL 如果遇到 Map 中 key 值为空的数据,将抛出异常。 DROP 将丢弃 Map 中 key 值为空的数据项。 LITERAL 将使用字符串常量来替换 Map 中的空 key 值。 字符串常量的值由 debezium-json.map-null-key.literal 定义。 |
debezium-json.map-null-key.literal | 选填 | null | String | 当 ‘debezium-json.map-null-key.mode’ 是 LITERAL 的时候,指定字符串常量替换 Map中的空 key 值。 |
debezium-json.encode.decimal-as-plain-number | 选填 | false | Boolean | 将所有 DECIMAL 类型的数据保持原状,不使用科学计数法表示。例:0.000000027 默认会表示为 2.7E-8 。当此选项设为 true 时,则会表示为 0.000000027 。 |
支持:
Canal 是一个 CDC(ChangeLog Data Capture,变更日志数据捕获)工具,可以实时地将 MySQL 变更传输到其他系统。
Canal 为变更日志提供了统一的数据格式,并支持使用 JSON
或 protobuf
序列化消息(Canal 默认使用 protobuf)。
Flink 支持将 Canal 的 JSON 消息解析为 INSERT / UPDATE / DELETE
消息到 Flink SQL 系统中。在很多情况下,这个特性非常有用,例如
Flink 还支持将 Flink SQL 中的 INSERT / UPDATE / DELETE
消息编码为 Canal 格式的 JSON
消息,输出到 Kafka 等存储中。
但需要注意的是,目前 Flink 还不支持将 UPDATE_BEFORE
和 UPDATE_AFTER
合并为一条 UPDATE
消息。
因此,Flink 将 UPDATE_BEFORE
和 UPDATE_AFTER
分别编码为 DELETE
和 INSERT
注意:未来会支持 Canal protobuf 类型消息的解析以及输出 Canal 格式的消息。
为了使用Canal格式,使用自动化构建工具(如Maven或SBT)的项目和使用SQL JAR包的SQL Client都需要以下依赖项。
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-jsonartifactId>
<version>1.16.0version>
dependency>
注意自己使用的 flink 版本。
注意:有关如何部署 Canal 以将变更日志同步到消息队列,请参阅 Canal 文档。
{
"data": [
{
"id": "111",
"name": "scooter",
"description": "Big 2-wheel scooter",
"weight": "5.18"
}
],
"database": "inventory",
"es": 1589373560000,
"id": 9,
"isDdl": false,
"mysqlType": {
"id": "INTEGER",
"name": "VARCHAR(255)",
"description": "VARCHAR(512)",
"weight": "FLOAT"
},
"old": [
{
"weight": "5.15"
}
],
"pkNames": [
"id"
],
"sql": "",
"sqlType": {
"id": 4,
"name": 12,
"description": 12,
"weight": 7
},
"table": "products",
"ts": 1589373560798,
"type": "UPDATE"
}
注意:有关各个字段的含义,请参阅 Canal 文档。
MySQL products 表有4列(id,name,description 和 weight)。
上面的 JSON 消息是 products 表上的一个更新事件,表示 id = 111 的行数据上 weight 字段值从5.15
变更成为 5.18
。
假设消息已经同步到了一个 Kafka 主题:products_binlog,那么就可以使用以下DDL来从这个主题消费消息并解析变更事件。
CREATE TABLE topic_products (
-- 元数据与 MySQL "products" 表完全相同
id BIGINT,
name STRING,
description STRING,
weight DECIMAL(10, 2)
) WITH (
'connector' = 'kafka',
'topic' = 'products_binlog',
'properties.bootstrap.servers' = 'localhost:9092',
'properties.group.id' = 'testGroup',
'format' = 'canal-json' -- 使用 canal-json 格式
)
将 Kafka 主题注册成 Flink 表之后,就可以将 Canal 消息用作变更日志源。
-- 关于MySQL "products" 表的实时物化视图
-- 计算相同产品的最新平均重量
SELECT name, AVG(weight) FROM topic_products GROUP BY name;
-- 将 MySQL "products" 表的所有数据和增量更改同步到
-- Elasticsearch "products" 索引以供将来搜索
INSERT INTO elasticsearch_products
SELECT * FROM topic_products;
以下format元数据可以在表定义中作为只读虚拟(VIRTUAL)列。
注意:只有在对应的连接器可以传递 format 元数据时,format 元数据属性才可用。目前,只有 kafka 连接器可以暴露元数据属性到他的 value format。
键 | 数据类型 | 描述 |
---|---|---|
database | STRING NULL | 原始数据库名。和Canal数据中的database 属性一致。 |
table | STRING NULL | 原始数据库表名。和Canal数据中的table 属性一致。 |
sql-type | MAP |
SQL type的map表。和Canal数据中的sqlType 属性一致。 |
pk-names | ARRAY |
主键名称的数组。和Canal数据中的pkNames 属性一致。 |
ingestion-timestamp | TIMESTAMP_LTZ(3) NULL | 连接器处理事件的时间戳。和Canal数据中的ts 属性一致。 |
下面的例子展示了如何在Kafka中访问Canal元数据字段:
CREATE TABLE KafkaTable (
origin_database STRING METADATA FROM 'value.database' VIRTUAL,
origin_table STRING METADATA FROM 'value.table' VIRTUAL,
origin_sql_type MAP<STRING, INT> METADATA FROM 'value.sql-type' VIRTUAL,
origin_pk_names ARRAY<STRING> METADATA FROM 'value.pk-names' VIRTUAL,
origin_ts TIMESTAMP(3) METADATA FROM 'value.ingestion-timestamp' 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',
'value.format' = 'canal-json'
);
选项 | 要求 | 默认 | 类型 | 描述 |
---|---|---|---|---|
format | 必选 | (none) | String | 指定要使用的格式,此处应为 canal-json 。 |
canal-json.ignore-parse-errors | 可选 | false | Boolean | 当解析异常时,是跳过当前字段或行,还是抛出错误失败(默认为 false,即抛出错误失败)。如果忽略字段的解析异常,则会将该字段值设置为null 。 |
canal-json.timestamp-format.standard | 可选 | SQL | String | 指定输入和输出时间戳格式。当前支持的值是 SQL 和 ISO-8601 。 SQL:将解析 yyyy-MM-dd HH:mm:ss.s{precision} 格式的输入时间戳,例如 ‘2020-12-30 12:13:14.123’,并以相同格式输出时间戳。 ISO-8601:将解析 yyyy-MM-ddTHH:mm:ss.s{precision} 格式的输入时间戳,例如 ‘2020-12-30T12:13:14.123’,并以相同的格式输出时间戳。 |
canal-json.map-null-key.mode | 可选 | FAIL | String | 指定处理 Map 中 key 值为空的方法. 当前支持的值有 FAIL, DROP 和 LITERAL。 FAIL:如果遇到 Map 中 key 值为空的数据,将抛出异常。 DROP:将丢弃 Map 中 key 值为空的数据项。 LITERAL:将使用字符串常量来替换 Map 中的空 key 值。字符串常量的值由 canal-json.map-null-key.literal 定义。 |
canal-json.map-null-key.literal | 可选 | null | String | 当 canal-json.map-null-key.mode 是 LITERAL 的时候,指定字符串常量替换 Map 中的空 key 值。 |
canal-json.encode.decimal-as-plain-number | 可选 | false | Boolean | 将所有 DECIMAL 类型的数据保持原状,不使用科学计数法表示。例:0.000000027 默认会表示为 2.7E-8 。当此选项设为 true 时,则会表示为 0.000000027 。 |
canal-json.database.include | 可选 | (none) | String | 一个可选的正则表达式,通过正则匹配 Canal 记录中的 database 元字段,仅读取指定数据库的 changelog 记录。正则字符串与 Java 的 Pattern 兼容。 |
canal-json.table.include | 可选 | (none) | String | 一个可选的正则表达式,通过正则匹配 Canal 记录中的 table 元字段,仅读取指定表的 changelog 记录。正则字符串与 Java 的 Pattern 兼容。 |
重复的变更事件
在正常的操作环境下,Canal 应用能以 exactly-once
的语义投递每条变更事件。然而,当有故障发生时,Canal 应用只能保证 at-least-once
的投递语义。这也意味着,在非正常情况下,Canal 可能会投递重复的变更事件到消息队列中,当 Flink 从消息队列中消费的时候就会得到重复的事件。
这可能会导致 Flink 查询的运行得到错误的结果或者非预期的异常。
因此,建议在这种情况下,建议在这种情况下,将作业参数 table.exec.source.cdc-events-duplicate
设置成 true,并在该 source 上定义 PRIMARY KEY
。框架会生成一个额外的有状态算子,使用该 primary key
来对变更事件去重并生成一个规范化的 changelog 流。
目前,Canal Format 使用 JSON Format 进行序列化和反序列化。 有关数据类型映射的更多详细信息,请参阅 JSON Format 文档。
支持:
Maxwell是一个CDC (Changelog变更数据捕捉)工具,可以实时从MySQL流到Kafka, Kinesis和其他流连接器。Maxwell为变更日志提供了统一的数据格式,并支持使用 JSON 序列化消息。
Flink支持将Maxwell JSON消息解释为INSERT/UPDATE/DELETE
消息到Flink SQL系统中。在许多情况下,这个特性是很有用的,例如
Flink还支持将Flink SQL中的INSERT/UPDATE/DELETE
消息编码为Maxwell JSON
消息,并发送到Kafka等外部系统。 但是,目前Flink还不能将UPDATE_BEFORE
和UPDATE_AFTER
合并成一个单独的UPDATE
消息。因此,Flink将UPDATE_BEFORE
和UDPATE_AFTER
编码为DELETE
和INSERT
Maxwell消息。
为了使用Maxwell格式,以下依赖项对于使用自动化构建工具(如Maven或SBT)的项目和带有SQL JAR包的SQL Client都是必需的。
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-jsonartifactId>
<version>1.16.0version>
dependency>
注意自己使用的 flink 版本。
注意:关于如何用Maxwell JSON同步changelog流到Kafka主题,请参考Maxwell文档。
Maxwell为changelog流提供了统一的格式,下面是一个简单的例子,用于从JSON格式的MySQL products表中获取更新操作。
{
"database": "test",
"table": "e",
"type": "insert",
"ts": 1477053217,
"xid": 23396,
"commit": true,
"position": "master.000006:800911",
"server_id": 23042,
"thread_id": 108,
"primary_key": [
1,
"2016-10-21 05:33:37.523000"
],
"primary_key_columns": [
"id",
"c"
],
"data": {
"id": 111,
"name": "scooter",
"description": "Big 2-wheel scooter",
"weight": 5.15
},
"old": {
"weight": 5.18
}
}
注意:关于每个字段的含义,请参考Maxwell文档。
MySQL products表有4列id, name, description 和weight)。 上面的JSON消息是products表上的更新更改事件,其中id = 111行的weight值从5.18
更改为5.15
。 假设这个消息同步到Kafka主题products_binlog,则可以使用下面的DDL来消费这个主题并解释变化事件。
CREATE TABLE topic_products (
-- schema和MySQL的"products"表完全一致
id BIGINT,
name STRING,
description STRING,
weight DECIMAL(10, 2)
) WITH (
'connector' = 'kafka',
'topic' = 'products_binlog',
'properties.bootstrap.servers' = 'localhost:9092',
'properties.group.id' = 'testGroup',
'format' = 'maxwell-json'
)
将主题注册为Flink表之后,就可以将Maxwell消息作为更改日志源使用了。
-- 关于MySQL "products" 表的实时物化视图
-- 计算相同产品的最新平均重量
SELECT name, AVG(weight) FROM topic_products GROUP BY name;
-- 将 MySQL "products" 表的所有数据和增量更改同步到 Elasticsearch "products" 索引以供将来搜索
INSERT INTO elasticsearch_products
SELECT * FROM topic_products;
下面的 format 元数据可以在表定义的只读虚拟(VIRTUAL)列中使用。
注意:只有在对应的连接器可以传递 format 元数据时,format 元数据属性才可用。目前,只有 kafka 连接器可以暴露元数据属性到他的 value format。
Key | 数据类型 | 描述 |
---|---|---|
database | STRING NULL | 原始数据库名称,如果可用,则对应于 Maxwell 数据中的database 字段。 |
table | STRING NULL | 原始数据库表名称,如果可用,则对应于 Maxwell 数据中的table 字段。 |
primary-key-columns | ARRAY |
主键名称数组,如果可用,则对应于 Maxwell 数据中的primary_key_columns 属性。 |
ingestion-timestamp | TIMESTAMP_LTZ(3) NULL | 连接器处理事件的时间戳。如果可用,则对应于 Maxwell 数据中的 ts 属性。 |
下面的案例展示如果访问 kafka 中 Maxwell 元数据属性:
CREATE TABLE KafkaTable (
origin_database STRING METADATA FROM 'value.database' VIRTUAL,
origin_table STRING METADATA FROM 'value.table' VIRTUAL,
origin_primary_key_columns ARRAY<STRING> METADATA FROM 'value.primary-key-columns' VIRTUAL,
origin_ts TIMESTAMP(3) METADATA FROM 'value.ingestion-timestamp' 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',
'value.format' = 'maxwell-json'
);
选项 | 要求 | 默认 | 类型 | 描述 |
---|---|---|---|---|
format | 必选 | (none) | String | 指定要使用的格式,此处应为 maxwell-json 。 |
maxwell-json.ignore-parse-errors | 可选 | false | Boolean | 当解析异常时,是跳过当前字段或行,还是抛出错误失败(默认为 false,即抛出错误失败)。如果忽略字段的解析异常,则会将该字段值设置为null。 |
maxwell-json.timestamp-format.standard | 可选 | SQL | String | 指定输入和输出时间戳格式。当前支持的值是 SQL 和 ISO-8601。 SQL:将解析 yyyy-MM-dd HH:mm:ss.s{precision} 格式的输入时间戳,例如 ‘2020-12-30 12:13:14.123’,并以相同格式输出时间戳。 ISO-8601:将解析 yyyy-MM-ddTHH:mm:ss.s{precision} 格式的输入时间戳,例如 ‘2020-12-30T12:13:14.123’,并以相同的格式输出时间戳。 |
maxwell-json.map-null-key.mode | 可选 | FAIL | String | 指定处理 Map 中 key 值为空的方法. 当前支持的值有 FAIL, DROP 和 LITERAL。 FAIL:如果遇到 Map 中 key 值为空的数据,将抛出异常。 DROP:将丢弃 Map 中 key 值为空的数据项。 LITERAL: 将使用字符串常量来替换 Map 中的空 key 值。字符串常量的值由 canal-json.map-null-key.literal 定义。 |
maxwell-json.map-null-key.literal | 可选 | null | String | 当 canal-json.map-null-key.mode 是 LITERAL 的时候,指定字符串常量替换 Map 中的空 key 值。 |
maxwell-json.encode.decimal-as-plain-number | 可选 | false | Boolean | 将所有 DECIMAL 类型的数据保持原状,不使用科学计数法表示。例:0.000000027 默认会表示为 2.7E-8 。当此选项设为 true 时,则会表示为 0.000000027 。 |
重复的变更事件
在正常的操作环境下,Maxwell 应用能以 exactly-once
的语义投递每条变更事件。当 Maswell 程序使用 at-least-once 语义运行时,程序可能会投递重复的变更事件到 kafka,当 Flink 从消息队列中消费的时候就会得到重复的事件。 这可能会导致 Flink 查询的运行得到错误的结果或者非预期的异常。
因此,建议在这种情况下,建议在这种情况下,将作业参数 table.exec.source.cdc-events-duplicate
设置成 true,并在该 source 上定义 PRIMARY KEY
。框架会生成一个额外的有状态算子,使用该 primary key
来对变更事件去重并生成一个规范化的 changelog 流。
目前,Maxwell 格式使用 JSON 来序列化和反序列化,请参考 JSON Format 文档来获取更多有关数据类型映射的细节。
支持:
Oracle GoldenGate(简称 ogg)
是一个提供实时数据转化平台的管理服务,使用复制的方式保证数据高可用以及实时分析。
消费者可以设计、执行功能、并且监控他们的数据副本和流式数据处理方案,而无需收集或管理计算环境。
Ogg 对 changelog 数据提供了一个 format schema ,并且提供了 JSON 格式的序列化数据。
Flink 支持在 Flink SQL 系统中解析 Ogg JSON 数据为 INSERT/UPDATE/DELETE
数据,在很多情况下,这个特性是非常有用的,比如:
Flink 也支持在 Flink SQL 中编码 INSERT/UPDATE/DELETE
消息为 Ogg JSON
消息,并且发射到其他系统,比如 kafka 。
然而,Flink 目前还不能合并 UPDATE_BEFORE
和 UPDATE_AFTER
为单个 UPDATE
消息。因此,Flink 会将 UPDATE_BEFORE
和 UPDATE_AFTER
编码为 DELETE
和 INSERT
Ogg 消息。
Ogg Json
为了使用Ogg 格式,以下依赖项对于使用自动化构建工具(如Maven或SBT)的项目和带有SQL JAR包的SQL Client都是必需的。
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-jsonartifactId>
<version>1.16.0version>
dependency>
注意自己的 flink 版本。
注:请参考 Ogg Kafka 处理 文档
来了解怎么设置 Ogg Kafka 处理器来同步 changelog 数据到 kafka 主题。
Ogg 对 changelog 提供了统一的 format,下面是一个简单的案例,展示了从 Oracle PRODUCTS 表中捕捉更新操作数据为 JSON 格式:
{
"before": {
"id": 111,
"name": "scooter",
"description": "Big 2-wheel scooter",
"weight": 5.18
},
"after": {
"id": 111,
"name": "scooter",
"description": "Big 2-wheel scooter",
"weight": 5.15
},
"op_type": "U",
"op_ts": "2020-05-13 15:40:06.000000",
"current_ts": "2020-05-13 15:40:07.000000",
"primary_keys": [
"id"
],
"pos": "00000000000000000000143",
"table": "PRODUCTS"
}
注:请参考 Debezium 文档来了解每个属性的含义。
Oracle PRODUCTS 表有4个字段 (id, name, description, weight),上面的 JSON 数据是 PRODUCTS 表的一个更新变更事件,
id 为 111 的 weight 值从 5.18
变成了 5.15
。假设这个数据同步到了 kafka 的 products_ogg 主题,然后我们就可以使用下面的 DDL 语句消费这个主题,并解析这个变更事件。
CREATE TABLE topic_products (
-- schema和 oracle 的 "products" 表是完全相同的
id BIGINT,
name STRING,
description STRING,
weight DECIMAL(10, 2)
) WITH (
'connector' = 'kafka',
'topic' = 'products_ogg',
'properties.bootstrap.servers' = 'localhost:9092',
'properties.group.id' = 'testGroup',
'format' = 'ogg-json'
)
将主题注册为 flink 表之后,就可以将 Ogg 消息作为 changelog source 来消费了。
-- Oracle "PRODUCTS" 表的实时物化视图,该视图计算了镶贴工产品最新的平均重量
SELECT name, AVG(weight)
FROM topic_products
GROUP BY name;
-- 同步 Oracle "PRODUCTS" 表的所有数据和增量变更数据到 Elasticsearch 的 "products" 索引中,以便将来进行搜索
INSERT INTO elasticsearch_products
SELECT *
FROM topic_products;
下面的元数据可以暴露到表定义的只读虚拟(VIRTUAL)字段中。
注意:只有在对应的连接器可以传递 format 元数据时,format 元数据属性才可用。目前,只有 kafka 连接器可以暴露元数据属性到他的 value format。
Key | 数据类型 | 描述 |
---|---|---|
table | STRING NULL | 表的全限定名称。表的权限名称格式为:CATALOG NAME.SCHEMA NAME.TABLE NAME |
primary-keys | ARRAY |
源表主键字段名称数组,如果 includePrimaryKeys 配置设置为 true ,则主键属性值只包含在 JSON 格式的输出数据中。 |
ingestion-timestamp | TIMESTAMP_LTZ(6) NULL | 连接器处理事件的时间戳,对应 Ogg 数据中的 current_ts 属性。 |
event-timestamp | TIMESTAMP_LTZ(6) NULL | source 系统创建事件的时间戳,对应于 Ogg 数据中的 op_ts 属性。 |
下面的案例展示如何访问 kafka 中 Ogg 元数据属性:
CREATE TABLE KafkaTable (
origin_ts TIMESTAMP(3) METADATA FROM 'value.ingestion-timestamp' VIRTUAL,
event_time TIMESTAMP(3) METADATA FROM 'value.event-timestamp' VIRTUAL,
origin_table STRING METADATA FROM 'value.table' VIRTUAL,
primary_keys ARRAY<STRING> METADATA FROM 'value.primary-keys' 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',
'value.format' = 'ogg-json'
);
参数 | 要求 | 默认值 | 类型 | 描述 |
---|---|---|---|---|
format | 必选 | (none) | String | 指定使用的 format ,这儿应该为:ogg-json |
ogg-json.ignore-parse-errors | 可选 | false | Boolean | 跳过转化失败的属性和行而不是失败,如果遇到错误,属性值将被设置为 null 。 |
ogg-json.timestamp-format.standard | 可选 | SQL | String | 指定输入和输出时间戳的格式,目前支持的值为:SQL 、 ISO-8601 。 SQL:转化时间戳为 yyyy-MM-dd HH:mm:ss.s{precision} 格式,比如: ‘2020-12-30 12:13:14.123’。 ISO-8601:转化时间戳为 yyyy-MM-ddTHH:mm:ss.s{precision} 格式,比如:‘2020-12-30T12:13:14.123’。 |
ogg-json.map-null-key.mode | 可选 | FAIL | String | 指定 map 类型数据遇到 key 值为 null 时的处理方式。目前支持的值为:FAIL、DROP、LITERAL 。 FAIL:遇到 map 数据中的 key 为 null 时抛出异常。 DROP:删除 map 数据中的 key 为 null 的 entry。 LITERAL:替换 key 为 null 值的字符串字面量。字符串字面量值通过 ogg-json.map-null-key.literal 选项定义。 |
ogg-json.map-null-key.literal | 可选 | null | String | 指定当 ogg-json.map-null-key.mode 选项值为 LITERAL 时,要替换为的字符串字面量值。 |
目前,Ogg 格式使用 JSON 格式来序列化和反序列化。
请参考 JSON 格式文档来获取更多有关数据类型映射的细节。
支持:
Apache Parquet 格式允许读写 Parquet 数据.
为了使用Parquet格式,使用自动化构建工具(如Maven或SBT)的项目和使用SQL JAR包的SQL Client都需要以下依赖项。
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-parquetartifactId>
<version>1.16.0version>
dependency>
注意自己使用的 flink 和 scala 版本。
以下为用 Filesystem 连接器和 Parquet 格式创建表的示例:
CREATE TABLE user_behavior (
user_id BIGINT,
item_id BIGINT,
category_id BIGINT,
behavior STRING,
ts TIMESTAMP(3),
dt STRING
) PARTITIONED BY (dt) WITH (
'connector' = 'filesystem',
'path' = '/tmp/user_behavior',
'format' = 'parquet'
)
参数 | 是否必须 | 默认值 | 类型 | 描述 |
---|---|---|---|---|
format | 必选 | (none) | String | 指定使用的格式,此处应为 parquet 。 |
parquet.utc-timezone | 可选 | false | Boolean | 使用 UTC 时区或本地时区在纪元时间和 LocalDateTime 之间进行转换。Hive 0.x/1.x/2.x 使用本地时区,但 Hive 3.x 使用 UTC 时区。 |
Parquet 格式也支持 ParquetOutputFormat
的配置。 例如, 可以配置 parquet.compression=GZIP
来开启 gzip 压缩。
目前,Parquet 格式类型映射与 Apache Hive 兼容,但与 Apache Spark 有所不同:
下表列举了 Flink 中的数据类型与 JSON 中的数据类型的映射关系。
Flink 数据类型 | Parquet 类型 | Parquet 逻辑类型 |
---|---|---|
CHAR / VARCHAR / STRING | BINARY | UTF8 |
BOOLEAN | BOOLEAN | |
BINARY / VARBINARY | BINARY | |
DECIMAL | FIXED_LEN_BYTE_ARRAY | DECIMAL |
TINYINT | INT32 | INT_8 |
SMALLINT | INT32 | INT_16 |
INT | INT32 | |
BIGINT | INT64 | |
FLOAT | FLOAT | |
DOUBLE | DOUBLE | |
DATE | INT32 | DATE |
TIME | INT32 | TIME_MILLIS |
TIMESTAMP | INT96 | |
ARRAY | - | LIST |
MAP | - | MAP |
ROW | - | STRUCT |
支持:
Apache Orc Format 允许读写 ORC 数据。
为了使用ORC格式,以下依赖项对于使用自动化构建工具(如Maven或SBT)的项目和带有SQL JAR包的SQL Client都是必需的。
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-orcartifactId>
<version>1.16.0version>
dependency>
注意自己使用的 flink 和 scala 版本。
下面是一个用 Filesystem connector 和 Orc format 创建表的例子
CREATE TABLE user_behavior (
user_id BIGINT,
item_id BIGINT,
category_id BIGINT,
behavior STRING,
ts TIMESTAMP(3),
dt STRING
) PARTITIONED BY (dt) WITH (
'connector' = 'filesystem',
'path' = '/tmp/user_behavior',
'format' = 'orc'
)
参数 | 是否必选 | 默认值 | 类型 | 描述 |
---|---|---|---|---|
format | 必选 | (none) | String | 指定要使用的格式,这里应该是 orc 。 |
Orc 格式也支持来源于 Table properties 的表属性。 举个例子,你可以设置 orc.compress=SNAPPY
来允许spappy压缩。
Orc 格式类型的映射和 Apache Hive 是兼容的。下面的表格列出了 Flink 类型的数据和 Orc 类型的数据的映射关系。
Flink 数据类型 | Orc 物理类型 | Orc 逻辑类型 |
---|---|---|
CHAR | bytes | CHAR |
VARCHAR | bytes | VARCHAR |
STRING | bytes | STRING |
BOOLEAN | long | BOOLEAN |
BYTES | bytes | BINARY |
DECIMAL | decimal | DECIMAL |
TINYINT | long | BYTE |
SMALLINT | long | SHORT |
INT | long | INT |
BIGINT | long | LONG |
FLOAT | double | FLOAT |
DOUBLE | double | DOUBLE |
DATE | long | DATE |
TIMESTAMP | timestamp | TIMESTAMP |
ARRAY | - | LIST |
MAP | - | MAP |
ROW | - | STRUCT |
支持:
Raw format 允许读写原始(基于字节)数据作为单个列的值。
注意: 这种格式会将 null
值编码成 byte[]
类型的 null
,因此在 upsert-kafka 中使用时可能会有限制,因为 upsert-kafka 将 null 值视为删除消息(在key上删除)。
因此,如果该字段可能有 null 值,我们建议避免使用 upsert-kafka 连接器和 raw format 作为 value.format。
Raw format 连接器是内置的,不需要添加额外的连接器依赖。
比如在 Kafka 中有以下原始日志数据,希望使用 Flink SQL 读取和分析此类数据。
47.29.201.179 - - [28/Feb/2019:13:17:10 +0000] "GET /?p=1 HTTP/2.0" 200 5316 "https://domain.com/?p=1" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36" "2.75"
下面的代码创建了一张表,使用 raw format 以 UTF-8 编码的形式从中读取(也可以写入)底层的 Kafka topic 主题数据为字符串:
CREATE TABLE nginx_log (
log STRING
) WITH (
'connector' = 'kafka',
'topic' = 'nginx_log',
'properties.bootstrap.servers' = 'localhost:9092',
'properties.group.id' = 'testGroup',
'format' = 'raw'
)
将原始数据读取为纯字符串,之后使用用户自定义函数将其分为多个字段进行进一步分析。例如示例中的 my_split。
SELECT t.hostname, t.datetime, t.url, t.browser, ...
FROM
(
SELECT my_split(log) as t FROM nginx_log
)
;
相对应的,也可以将一个字符串类型的列以 UTF-8 编码的形式写入 Kafka topic。
参数 | 是否必选 | 默认值 | 类型 | 描述 |
---|---|---|---|---|
format | 必选 | (none) | String | 指定要使用的格式, 这里应该是 raw。 |
raw.charset | 可选 | UTF-8 | String | 指定字符集来编码文本字符串。 |
raw.endianness | 可选 | big-endian | String | 指定字节序来编码数字值的字节。有效值为 big-endian 和 little-endian 。更多细节可查阅字节序。 |
下表详细说明了这种格式支持的 SQL 类型,包括用于编码和解码的序列化类和反序列化类的详细信息。
Flink SQL 类型 | 值 |
---|---|
CHAR / VARCHAR / STRING | UTF-8(默认)编码的文本字符串。 编码字符集可以通过 raw.charset 进行配置。 |
BINARY / VARBINARY / BYTES | 字节序列本身。 |
BOOLEAN | 表示布尔值的单个字节,0表示 false, 1 表示 true。 |
TINYINT | 有符号数字值的单个字节。 |
SMALLINT | 采用big-endian (默认)编码的两个字节。字节序可以通过 raw.endianness 配置。 |
INT | 采用 big-endian (默认)编码的四个字节。字节序可以通过 raw.endianness 配置。 |
BIGINT | 采用 big-endian (默认)编码的八个字节。字节序可以通过 raw.endianness 配置。 |
FLOAT | 采用 IEEE 754 格式和 big-endian (默认)编码的四个字节。字节序可以通过 raw.endianness 配置。 |
DOUBLE | 采用 IEEE 754 格式和 big-endian (默认)编码的八个字节。字节序可以通过 raw.endianness 配置。 |
RAW | 通过 RAW 类型的底层 TypeSerializer 序列化的字节序列。 |