本文翻译自官网:Connect to External Systems https://ci.apache.org/projects/flink/flink-docs-release-1.9/dev/table/connect.html
注:本文对应代码段为多种格式,影响文章篇幅,所以只选取其中一种类似列入,全部内容见官网对应页面
Flink 的 Table API 和 SQL 程序可以连接到其他外部系统,以读取和写入批处理表和流式表。表源提供对存储在外部系统(例如数据库,键值存储,消息队列或文件系统)中的数据的访问。表接收器将表发送到外部存储系统。根据源和接收器的类型,它们支持不同的格式,例如 CSV,Parquet或ORC。
本页介绍如何声明内置表源/表接收器以及如何在 Flink中注册它们。注册源或接收器后,可以通过 Table API 和 SQL 语句对其进行访问。
注意如果要实现自己的定制表源或接收器,请查看用户定义的源和接收器页面。
依赖
下表列出了所有可用的连接器和格式。它们的相互兼容性在表连接器和表格式的相应部分中进行了标记。下表提供了使用构建自动化工具(例如Maven或SBT)和带有SQL JAR捆绑包的SQL Client的两个项目的依赖项信息。
连接器
Name | Version | Maven dependency | SQL Client JAR |
---|---|---|---|
Filesystem | Built-in | Built-in | |
Elasticsearch | 6 | flink-connector-elasticsearch6 |
Download |
Apache Kafka | 0.8 | flink-connector-kafka-0.8 |
Not available |
Apache Kafka | 0.9 | flink-connector-kafka-0.9 |
Download |
Apache Kafka | 0.10 | flink-connector-kafka-0.10 |
Download |
Apache Kafka | 0.11 | flink-connector-kafka-0.11 |
Download |
Apache Kafka | 0.11+ (universal ) |
flink-connector-kafka |
Download |
HBase | 1.4.3 | flink-hbase |
Download |
JDBC | flink-jdbc |
Download |
格式
Name | Maven dependency | SQL Client JAR |
---|---|---|
Old CSV (for files) | Built-in | Built-in |
CSV (for Kafka) | flink-csv |
Download |
JSON | flink-json |
Download |
Apache Avro | flink-avro |
Download |
总览
从Flink 1.6开始,与外部系统的连接声明与实际实现分开了。
可以指定连接
- 以编程方式使用org.apache.flink.table.descriptors下的Table&SQL API的Descriptor 。
- 通过用于SQL客户端的YAML配置文件声明。
这不仅可以更好地统一API和SQL Client,还可以在自定义实现的情况下更好地扩展而不更改实际声明。
每个声明都类似于SQL CREATE TABLE语句。 可以定义表的名称,表的结构,连接器以及用于连接到外部系统的数据格式。
连接器描述了存储表数据的外部系统。 可以在此处声明诸如Apacha Kafka之类的存储系统或常规文件系统。 连接器可能已经提供了带有字段和结构的固定格式。
某些系统支持不同的数据格式。 例如,存储在Kafka或文件中的表可以使用CSV,JSON或Avro对行进行编码。 数据库连接器可能需要此处的表结构。 每个连接器都记录了存储系统是否需要格式的定义。 不同的系统还需要不同类型的格式(例如,面向列的格式与面向行的格式)。 该文档说明了哪些格式类型和连接器兼容。
表结构定义了公开给SQL查询的表的结构。 它描述了源如何将数据格式映射到表模式,反之亦然。 该模式可以访问由连接器或格式定义的字段。 它可以使用一个或多个字段来提取或插入时间属性。 如果输入字段没有确定性的字段顺序,则该结构将明确定义列名称,其顺序和来源。
随后的部分将更详细地介绍每个定义部分(连接器,格式和结构)。 以下示例显示如何传递它们:
Java、Scala 定义 tableEnvironment .connect(...) .withFormat(...) .withSchema(...) .inAppendMode() .registerTableSource("MyTable")
表格的类型(源,接收器或两者)决定了表格的注册方式。 对于两种表类型,表源和表接收器都以相同的名称注册。 从逻辑上讲,这意味着我们可以像读取常规DBMS中的表一样读取和写入该表。
对于流查询,更新模式声明了如何在动态表和存储系统之间进行通信以进行连续查询。
以下代码显示了如何连接到Kafka以读取Avro记录的完整示例。
Java、Scala 定义
tableEnvironment // declare the external system to connect to .connect( new Kafka() .version("0.10") .topic("test-input") .startFromEarliest() .property("zookeeper.connect", "localhost:2181") .property("bootstrap.servers", "localhost:9092") ) // declare a format for this system .withFormat( new Avro() .avroSchema( "{" + " \"namespace\": \"org.myorganization\"," + " \"type\": \"record\"," + " \"name\": \"UserMessage\"," + " \"fields\": [" + " {\"name\": \"timestamp\", \"type\": \"string\"}," + " {\"name\": \"user\", \"type\": \"long\"}," + " {\"name\": \"message\", \"type\": [\"string\", \"null\"]}" + " ]" + "}" ) ) // declare the schema of the table .withSchema( new Schema() .field("rowtime", Types.SQL_TIMESTAMP) .rowtime(new Rowtime() .timestampsFromField("timestamp") .watermarksPeriodicBounded(60000) ) .field("user", Types.LONG) .field("message", Types.STRING) ) // specify the update-mode for streaming tables .inAppendMode() // register as source, sink, or both and under a name .registerTableSource("MyUserTable");
两种方式都将所需的连接属性转换为标准化的基于字符串的键值对。 所谓的表工厂根据键值对创建已配置的表源,表接收器和相应的格式。 搜索完全匹配的表工厂时,会考虑到所有可通过Java服务提供商接口(SPI)找到的表工厂。
如果找不到给定属性的工厂或多个工厂匹配,则将引发异常,并提供有关考虑的工厂和支持的属性的其他信息。
表结构
表结构定义列的名称和类型,类似于SQL CREATE TABLE语句的列定义。 此外,可以指定如何将列与表数据编码格式的字段进行映射。 如果列名应与输入/输出格式不同,则字段的来源可能很重要。 例如,一列user_name应该引用JSON格式的$$-user-name字段。 此外,需要使用该结构将类型从外部系统映射到Flink的表示形式。 如果是表接收器,则可确保仅将具有有效结构的数据写入外部系统。
以下示例显示了一个没有时间属性的简单架构,并且输入/输出到表列的一对一字段映射。
Java、Scala 定义
.withSchema( new Schema() .field("MyField1", Types.INT) // required: specify the fields of the table (in this order) .field("MyField2", Types.STRING) .field("MyField3", Types.BOOLEAN) )
对于每个字段,除列的名称和类型外,还可以声明以下属性:
Java、Scala 定义
.withSchema(
new Schema()
.field("MyField1", Types.SQL_TIMESTAMP)
.proctime() // optional: declares this field as a processing-time attribute
.field("MyField2", Types.SQL_TIMESTAMP)
.rowtime(...) // optional: declares this field as a event-time attribute
.field("MyField3", Types.BOOLEAN)
.from("mf3") // optional: original field in the input that is referenced/aliased by this field
)
使用无界流表时,时间属性至关重要。 因此,处理时间和事件时间(也称为“行时间”)属性都可以定义为架构的一部分。
有关Flink中时间处理(尤其是事件时间)的更多信息,我们建议使用常规事件时间部分。
行时间属性
为了控制表的事件时间行为,Flink提供了预定义的时间戳提取器和水印策略。
支持以下时间戳提取器:
Java、Scala
// Converts an existing LONG or SQL_TIMESTAMP field in the input into the rowtime attribute. .rowtime( new Rowtime() .timestampsFromField("ts_field") // required: original field name in the input ) // Converts the assigned timestamps from a DataStream API record into the rowtime attribute // and thus preserves the assigned timestamps from the source. // This requires a source that assigns timestamps (e.g., Kafka 0.10+). .rowtime( new Rowtime() .timestampsFromSource() ) // Sets a custom timestamp extractor to be used for the rowtime attribute. // The extractor must extend `org.apache.flink.table.sources.tsextractors.TimestampExtractor`. .rowtime( new Rowtime() .timestampsFromExtractor(...) )
支持以下水印策略:
Java、Scala
// Sets a watermark strategy for ascending rowtime attributes. Emits a watermark of the maximum // observed timestamp so far minus 1. Rows that have a timestamp equal to the max timestamp // are not late. .rowtime( new Rowtime() .watermarksPeriodicAscending() ) // Sets a built-in watermark strategy for rowtime attributes which are out-of-order by a bounded time interval. // Emits watermarks which are the maximum observed timestamp minus the specified delay. .rowtime( new Rowtime() .watermarksPeriodicBounded(2000) // delay in milliseconds ) // Sets a built-in watermark strategy which indicates the watermarks should be preserved from the // underlying DataStream API and thus preserves the assigned watermarks from the source. .rowtime( new Rowtime() .watermarksFromSource() )
确保始终声明时间戳和水印。 触发基于时间的操作需要水印。
类型字符串
由于类型信息仅在编程语言中可用,因此支持在YAML文件中定义以下类型字符串:
VARCHAR BOOLEAN TINYINT SMALLINT INT BIGINT FLOAT DOUBLE DECIMAL DATE TIME TIMESTAMP MAP<fieldtype, fieldtype> # generic map; e.g. MAP<VARCHAR, INT> that is mapped to Flink's MapTypeInfo MULTISET<fieldtype> # multiset; e.g. MULTISET<VARCHAR> that is mapped to Flink's MultisetTypeInfo PRIMITIVE_ARRAY<fieldtype> # primitive array; e.g. PRIMITIVE_ARRAY<INT> that is mapped to Flink's PrimitiveArrayTypeInfo OBJECT_ARRAY<fieldtype> # object array; e.g. OBJECT_ARRAY<POJO(org.mycompany.MyPojoClass)> that is mapped to # Flink's ObjectArrayTypeInfo ROW<fieldtype, ...> # unnamed row; e.g. ROW<VARCHAR, INT> that is mapped to Flink's RowTypeInfo # with indexed fields names f0, f1, ... ROW<fieldname fieldtype, ...> # named row; e.g., ROW<myField VARCHAR, myOtherField INT> that # is mapped to Flink's RowTypeInfo POJO<class> # e.g., POJO<org.mycompany.MyPojoClass> that is mapped to Flink's PojoTypeInfo ANY<class> # e.g., ANY<org.mycompany.MyClass> that is mapped to Flink's GenericTypeInfo ANY<class, serialized> # used for type information that is not supported by Flink's Table & SQL API
更新模式
对于流查询,需要声明如何在动态表和外部连接器之间执行转换。 更新模式指定应与外部系统交换的消息类型:
Append Mode: 在追加模式下,动态表和外部连接器仅交换INSERT消息。
Retract Mode: 在撤回模式下,动态表和外部连接器交换ADD和RETRACT消息。 INSERT更改被编码为ADD消息,DELETE更改为RETRACT消息,UPDATE更改为更新(先前)行的RETRACT消息,而ADD消息被更新(新)行的ADD消息。 在此模式下,与upsert模式相反,不得定义密钥。 但是,每个更新都包含两个效率较低的消息。
Upsert Mode: 在upsert模式下,动态表和外部连接器交换UPSERT和DELETE消息。 此模式需要一个(可能是复合的)唯一密钥,通过该密钥可以传播更新。 外部连接器需要了解唯一键属性,才能正确应用消息。 INSERT和UPDATE更改被编码为UPSERT消息。 DELETE更改为DELETE消息。 与撤回流的主要区别在于UPDATE更改使用单个消息进行编码,因此效率更高。
注意:每个连接器的文档都说明了支持哪些更新模式。
Java、Scala .connect(...) .inAppendMode() // otherwise: inUpsertMode() or inRetractMode()
另请参阅常规流概念文档。
表连接器
Flink提供了一组用于连接到外部系统的连接器。
请注意,并非所有连接器都可以批量和流式使用。 此外,并非每个流连接器都支持每种流模式。 因此,每个连接器都有相应的标记。 格式标签表示连接器需要某种类型的格式。
文件系统连接器
Source: Batch, Source: Streaming, Append Mode, Sink: Batch, Sink: Streaming, Append Mode Format: CSV-only
文件系统连接器允许从本地或分布式文件系统读取和写入。 文件系统可以定义为:
Java、Scala .connect( new FileSystem() .path("file:///path/to/whatever") // required: path to a file or directory )
文件系统连接器本身包含在Flink中,不需要其他依赖项。 需要指定一种相应的格式,以便在文件系统中读取和写入行。
注意:确保包括Flink File System特定的依赖项。
注意文件系统源和流接收器仅是实验性的。 将来,我们将支持实际的流传输用例,即目录监视和存储桶输出。
Kafka连接器
Source: Streaming Append Mode, Sink: Streaming Append Mode, Format: Serialization, Schema Format: Deserialization Schema
Kafka连接器允许在Apache Kafka主题之间进行读写。 可以定义如下:
.connect( new Kafka() .version("0.11") // required: valid connector versions are // "0.8", "0.9", "0.10", "0.11", and "universal" .topic("...") // required: topic name from which the table is read // optional: connector specific properties .property("zookeeper.connect", "localhost:2181") .property("bootstrap.servers", "localhost:9092") .property("group.id", "testGroup") // optional: select a startup mode for Kafka offsets .startFromEarliest() .startFromLatest() .startFromSpecificOffsets(...) // optional: output partitioning from Flink's partitions into Kafka's partitions .sinkPartitionerFixed() // each Flink partition ends up in at-most one Kafka partition (default) .sinkPartitionerRoundRobin() // a Flink partition is distributed to Kafka partitions round-robin .sinkPartitionerCustom(MyCustom.class) // use a custom FlinkKafkaPartitioner subclass )
Specify the start reading position: 默认情况下,Kafka源将从Zookeeper或Kafka代理中的已提交组偏移中开始读取数据。 您可以指定其他起始位置,这些位置与“ Kafka Consumers起始位置配置”部分中的配置相对应。
Flink-Kafka Sink Partitioning: 默认情况下,Kafka接收器最多可以写入与其自身并行性一样多的分区(每个并行的接收器实例都写入一个分区)。 为了将写入内容分配到更多分区或控制行到分区的路由,可以提供自定义接收器分区程序。 循环分区器对于避免不平衡分区很有用。 但是,这将导致所有Flink实例与所有Kafka代理之间的大量网络连接。
Consistency guarantees: 默认情况下,如果在启用检查点的情况下执行查询,则Kafka接收器会将具有至少一次保证的数据提取到Kafka主题中。
Kafka 0.10+ Timestamps: 从Kafka 0.10开始,Kafka消息具有时间戳作为元数据,用于指定何时将记录写入Kafka主题。 通过选择时间戳,可以将这些时间戳用于rowtime属性:分别是YAML中的from-source和Java / Scala中的timestampsFromSource()。
Kafka 0.11+ Versioning: 从Flink 1.7开始,Kafka连接器定义独立于硬编码的Kafka版本。 将通用连接器版本用作Flink Kafka连接器的通配符,该连接器与所有版本从0.11开始的Kafka兼容。
Elasticsearch连接器
Sink: Streaming Append Mode, Sink: Streaming Upsert Mode, Format: JSON-only
Elasticsearch连接器允许写入Elasticsearch搜索引擎的索引。
连接器可以在upsert模式下运行,以使用查询定义的密钥与外部系统交换UPSERT / DELETE消息。
对于 appen-only 查询,连接器还可以在追加模式下操作,以仅与外部系统交换INSERT消息。 如果查询未定义任何键,则Elasticsearch自动生成一个键。
连接器可以定义如下:
.connect( new Elasticsearch() .version("6") // required: valid connector versions are "6" .host("localhost", 9200, "http") // required: one or more Elasticsearch hosts to connect to .index("MyUsers") // required: Elasticsearch index .documentType("user") // required: Elasticsearch document type .keyDelimiter("$") // optional: delimiter for composite keys ("_" by default) // e.g., "$" would result in IDs "KEY1$KEY2$KEY3" .keyNullLiteral("n/a") // optional: representation for null fields in keys ("null" by default) // optional: failure handling strategy in case a request to Elasticsearch fails (fail by default) .failureHandlerFail() // optional: throws an exception if a request fails and causes a job failure .failureHandlerIgnore() // or ignores failures and drops the request .failureHandlerRetryRejected() // or re-adds requests that have failed due to queue capacity saturation .failureHandlerCustom(...) // or custom failure handling with a ActionRequestFailureHandler subclass // optional: configure how to buffer elements before sending them in bulk to the cluster for efficiency .disableFlushOnCheckpoint() // optional: disables flushing on checkpoint (see notes below!) .bulkFlushMaxActions(42) // optional: maximum number of actions to buffer for each bulk request .bulkFlushMaxSize("42 mb") // optional: maximum size of buffered actions in bytes per bulk request // (only MB granularity is supported) .bulkFlushInterval(60000L) // optional: bulk flush interval (in milliseconds) .bulkFlushBackoffConstant() // optional: use a constant backoff type .bulkFlushBackoffExponential() // or use an exponential backoff type .bulkFlushBackoffMaxRetries(3) // optional: maximum number of retries .bulkFlushBackoffDelay(30000L) // optional: delay between each backoff attempt (in milliseconds) // optional: connection properties to be used during REST communication to Elasticsearch .connectionMaxRetryTimeout(3) // optional: maximum timeout (in milliseconds) between retries .connectionPathPrefix("/v1") // optional: prefix string to be added to every REST communication )
Bulk flushing: 有关可选的刷新参数的特征的更多信息,请参见相应的低级文档。
Disabling flushing on checkpoint: 禁用后,接收器将不等待Elasticsearch在检查点上确认所有阻塞的操作请求。 因此,接收器不会为动作请求的至少一次传递提供任何有力的保证。
Key extraction: Flink自动从查询中提取有效键。 例如,查询SELECT a,b,c FROM t GROUP BY a,b定义了字段a和b的组合键。 Elasticsearch连接器通过使用关键字定界符按查询中定义的顺序连接所有关键字字段,为每一行生成一个文档ID字符串。 可以定义键字段的空文字的自定义表示形式。
注意:JSON格式定义了如何为外部系统编码文档,因此,必须将其添加为依赖项。
HBase连接器
Source: Batch, Sink: Batch, Sink: Streaming Append Mode, Sink: Streaming Upsert Mode, Temporal Join: Sync Mode
HBase连接器允许读取和写入HBase群集。
连接器可以在upsert模式下运行,以使用查询定义的密钥与外部系统交换UPSERT / DELETE消息。
对于 append-only 查询,连接器还可以在追加模式下操作,以仅与外部系统交换INSERT消息。
连接器可以定义如下:
connector: type: hbase version: "1.4.3" # required: currently only support "1.4.3" table-name: "hbase_table_name" # required: HBase table name zookeeper: quorum: "localhost:2181" # required: HBase Zookeeper quorum configuration znode.parent: "/test" # optional: the root dir in Zookeeper for HBase cluster. # The default value is "/hbase". write.buffer-flush: max-size: "10mb" # optional: writing option, determines how many size in memory of buffered # rows to insert per round trip. This can help performance on writing to JDBC # database. The default value is "2mb". max-rows: 1000 # optional: writing option, determines how many rows to insert per round trip. # This can help performance on writing to JDBC database. No default value, # i.e. the default flushing is not depends on the number of buffered rows. interval: "2s" # optional: writing option, sets a flush interval flushing buffered requesting # if the interval passes, in milliseconds. Default value is "0s", which means # no asynchronous flush thread will be scheduled.
Columns: HBase表中的所有列系列必须声明为ROW类型,字段名称映射到列 family 名称,而嵌套的字段名称映射到列 qualifier 名称。 无需在结构中声明所有族和限定符,用户可以声明必要的内容。 除ROW type字段外,原子类型的唯一一个字段(例如STRING,BIGINT)将被识别为表的行键。 行键字段的名称没有任何限制。
Temporary join: 针对HBase的查找联接不使用任何缓存; 始终总是通过HBase客户端直接查询数据。
Java/Scala/Python API: Java/Scala/Python APIs 还不支持
JDBC连接器
Source: Batch, Sink: Batch, Sink: Streaming Append Mode, Sink: Streaming Upsert Mode, Temporal Join: Sync Mode
JDBC连接器允许读取和写入JDBC客户端。
连接器可以在upsert模式下运行,以使用查询定义的密钥与外部系统交换UPSERT / DELETE消息。
对于 append-only 查询,连接器还可以在追加模式下操作,以仅与外部系统交换INSERT消息。
要使用JDBC连接器,需要选择一个实际的驱动程序来使用。 当前支持以下驱动程序:
支持的驱动:
Name | Group Id | Artifact Id | JAR |
---|---|---|---|
MySQL | mysql | mysql-connector-java | Download |
PostgreSQL | org.postgresql | postgresql | Download |
Derby | org.apache.derby | derby | Download |
连接器可以定义如下:
connector: type: jdbc url: "jdbc:mysql://localhost:3306/flink-test" # required: JDBC DB url table: "jdbc_table_name" # required: jdbc table name driver: "com.mysql.jdbc.Driver" # optional: the class name of the JDBC driver to use to connect to this URL. # If not set, it will automatically be derived from the URL. username: "name" # optional: jdbc user name and password password: "password" read: # scan options, optional, used when reading from table partition: # These options must all be specified if any of them is specified. In addition, partition.num must be specified. They # describe how to partition the table when reading in parallel from multiple tasks. partition.column must be a numeric, # date, or timestamp column from the table in question. Notice that lowerBound and upperBound are just used to decide # the partition stride, not for filtering the rows in table. So all rows in the table will be partitioned and returned. # This option applies only to reading. column: "column_name" # optional, name of the column used for partitioning the input. num: 50 # optional, the number of partitions. lower-bound: 500 # optional, the smallest value of the first partition. upper-bound: 1000 # optional, the largest value of the last partition. fetch-size: 100 # optional, Gives the reader a hint as to the number of rows that should be fetched # from the database when reading per round trip. If the value specified is zero, then # the hint is ignored. The default value is zero. lookup: # lookup options, optional, used in temporary join cache: max-rows: 5000 # optional, max number of rows of lookup cache, over this value, the oldest rows will # be eliminated. "cache.max-rows" and "cache.ttl" options must all be specified if any # of them is specified. Cache is not enabled as default. ttl: "10s" # optional, the max time to live for each rows in lookup cache, over this time, the oldest rows # will be expired. "cache.max-rows" and "cache.ttl" options must all be specified if any of # them is specified. Cache is not enabled as default. max-retries: 3 # optional, max retry times if lookup database failed write: # sink options, optional, used when writing into table flush: max-rows: 5000 # optional, flush max size (includes all append, upsert and delete records), # over this number of records, will flush data. The default value is "5000". interval: "2s" # optional, flush interval mills, over this time, asynchronous threads will flush data. # The default value is "0s", which means no asynchronous flush thread will be scheduled. max-retries: 3 # optional, max retry times if writing records to database failed.
Upsert sink: Flink自动从查询中提取有效键。 例如,查询SELECT a,b,c FROM t GROUP BY a,b定义了字段a和b的组合键。 如果将JDBC表用作upsert接收器,请确保查询的键是基础数据库的唯一键集或主键之一。 这样可以保证输出结果符合预期。
Temporary Join: JDBC连接器可以在临时联接中用作查找源。 当前,仅支持同步查找模式。 如果指定了查找缓存选项(connector.lookup.cache.max-rows和connector.lookup.cache.ttl),则必须全部指定它们。 查找缓存用于通过首先查询缓存而不是将所有请求发送到远程数据库来提高临时连接JDBC连接器的性能。 但是,如果来自缓存,则返回的值可能不是最新的。 因此,这是吞吐量和正确性之间的平衡。
Writing: 默认情况下,connector.write.flush.interval为0s,connector.write.flush.max-rows为5000,这意味着对于低流量查询,缓冲的输出行可能不会长时间刷新到数据库。 因此,建议设置间隔配置。
表格格式
Flink提供了一组表格式,可与表连接器一起使用。
格式标签表示与连接器匹配的格式类型。
CSV格式
Format: Serialization Schema, Format: Deserialization Schema
CSV格式旨在符合Internet工程任务组(IETF)提出的RFC-4180(“逗号分隔值(CSV)文件的通用格式和MIME类型”)。
该格式允许读取和写入与给定格式模式对应的CSV数据。 格式结构可以定义为Flink类型,也可以从所需的表结构派生。
如果格式模式等于表结构,则也可以自动派生该结构。 这仅允许定义一次结构信息。 格式的名称,类型和字段的顺序由表的结构确定。 如果时间属性的来源不是字段,则将忽略它们。 表模式中的from定义被解释为以该格式重命名的字段。
CSV格式可以如下使用:
.withFormat( new Csv() // required: define the schema either by using type information .schema(Type.ROW(...)) // or use the table's schema .deriveSchema() .fieldDelimiter(';') // optional: field delimiter character (',' by default) .lineDelimiter("\r\n") // optional: line delimiter ("\n" by default; // otherwise "\r" or "\r\n" are allowed) .quoteCharacter('\'') // optional: quote character for enclosing field values ('"' by default) .allowComments() // optional: ignores comment lines that start with '#' (disabled by default); // if enabled, make sure to also ignore parse errors to allow empty rows .ignoreParseErrors() // optional: skip fields and rows with parse errors instead of failing; // fields are set to null in case of errors .arrayElementDelimiter("|") // optional: the array element delimiter string for separating // array and row element values (";" by default) .escapeCharacter('\\') // optional: escape character for escaping values (disabled by default) .nullLiteral("n/a") // optional: null literal string that is interpreted as a // null value (disabled by default) )
下表列出了可以读取和写入的受支持类型:
Supported Flink SQL Types |
---|
ROW |
VARCHAR |
ARRAY[_] |
INT |
BIGINT |
FLOAT |
DOUBLE |
BOOLEAN |
DATE |
TIME |
TIMESTAMP |
DECIMAL |
NULL (unsupported yet) |
Numeric types: 值应该是数字,但字面量“ null”也可以理解。 空字符串被视为null。 值也被修剪(开头/结尾随空白)。 数字是使用Java的valueOf语义解析的。 其他非数字字符串可能会导致解析异常。
String and time types: 值未修剪。 文字“ null”也可以理解。 时间类型必须根据Java SQL时间格式进行格式化,且精度为毫秒。 例如:日期为2018-01-01,时间为20:43:59,时间戳为2018-01-01 20:43:59.999。
Boolean type: 值应为布尔值(“ true”,“ false”)字符串或“ null”。 空字符串被解释为false。 值被修剪(开头/结尾随空白)。 其他值导致异常。
Nested types: 使用数组元素定界符可以为一级嵌套支持数组和行类型。
Primitive byte arrays: 基本字节数组以Base64编码表示形式处理。
Line endings: 对于行末未引号的字符串字段,即使对于基于行的连接器(如Kafka)也应忽略行尾。
Escaping and quoting: 下表显示了使用*进行转义和使用'进行引用的转义和引用如何影响字符串的解析的示例:
CSV Field | Parsed String |
---|---|
123*'4** |
123'4* |
'123''4**' |
123'4* |
'a;b*'c' |
a;b'c |
'a;b''c' |
a;b'c |
确保将CSV格式添加为依赖项。
JSON格式
Format: Serialization Schema, Format: Deserialization Schema
JSON格式允许读取和写入与给定格式结构相对应的JSON数据。 格式结构可以定义为Flink类型,JSON 结构或从所需的表结构派生。 Flink类型启用了更类似于SQL的定义并映射到相应的SQL数据类型。 JSON 格式允许更复杂和嵌套的结构。
如果格式结构等于表结构,则也可以自动派生该结构。 这仅允许定义一次结构信息。 格式的名称,类型和字段的顺序由表的结构确定。 如果时间属性的来源不是字段,则将忽略它们。 表结构中的from定义被解释为以该格式重命名的字段。
JSON格式可以如下使用:
.withFormat( new Json() .failOnMissingField(true) // optional: flag whether to fail if a field is missing or not, false by default // required: define the schema either by using type information which parses numbers to corresponding types .schema(Type.ROW(...)) // or by using a JSON schema which parses to DECIMAL and TIMESTAMP .jsonSchema( "{" + " type: 'object'," + " properties: {" + " lon: {" + " type: 'number'" + " }," + " rideTime: {" + " type: 'string'," + " format: 'date-time'" + " }" + " }" + "}" ) // or use the table's schema .deriveSchema() )
下表显示了JSON模式类型到Flink SQL类型的映射:
JSON schema | Flink SQL |
---|---|
object |
ROW |
boolean |
BOOLEAN |
array |
ARRAY[_] |
number |
DECIMAL |
integer |
DECIMAL |
string |
VARCHAR |
string with format: date-time |
TIMESTAMP |
string with format: date |
DATE |
string with format: time |
TIME |
string with encoding: base64 |
ARRAY[TINYINT] |
null |
NULL (unsupported yet) |
当前,Flink仅支持JSON模式规范draft-07的子集。 尚不支持联合类型(以及allOf,anyOf和not)。 仅支持oneOf和类型数组用于指定可为空性。
支持链接到文档中通用定义的简单引用,如以下更复杂的示例所示:
{ "definitions": { "address": { "type": "object", "properties": { "street_address": { "type": "string" }, "city": { "type": "string" }, "state": { "type": "string" } }, "required": [ "street_address", "city", "state" ] } }, "type": "object", "properties": { "billing_address": { "$ref": "#/definitions/address" }, "shipping_address": { "$ref": "#/definitions/address" }, "optional_address": { "oneOf": [ { "type": "null" }, { "$ref": "#/definitions/address" } ] } } }
Missing Field Handling: 默认情况下,缺少的JSON字段设置为null。 您可以启用严格的JSON解析,如果缺少字段,则将取消源(和查询)。
确保将JSON格式添加为依赖项。
Apache Avro格式
Format: Serialization Schema, Format: Deserialization Schema
Apache Avro格式允许读取和写入与给定格式模式相对应的Avro数据。 格式结构可以定义为Avro特定记录的完全限定的类名,也可以定义为Avro架构字符串。 如果使用了类名,则在运行时该类必须在类路径中可用。
Avro格式可以如下使用:
.withFormat( new Avro() // required: define the schema either by using an Avro specific record class .recordClass(User.class) // or by using an Avro schema .avroSchema( "{" + " \"type\": \"record\"," + " \"name\": \"test\"," + " \"fields\" : [" + " {\"name\": \"a\", \"type\": \"long\"}," + " {\"name\": \"b\", \"type\": \"string\"}" + " ]" + "}" ) )
Avro类型映射到相应的SQL数据类型。 仅支持联合类型用于指定可为空性,否则它们将转换为ANY类型。 下表显示了映射:
Avro schema | Flink SQL |
---|---|
record |
ROW |
enum |
VARCHAR |
array |
ARRAY[_] |
map |
MAP[VARCHAR, _] |
union |
non-null type or ANY |
fixed |
ARRAY[TINYINT] |
string |
VARCHAR |
bytes |
ARRAY[TINYINT] |
int |
INT |
long |
BIGINT |
float |
FLOAT |
double |
DOUBLE |
boolean |
BOOLEAN |
int with logicalType: date |
DATE |
int with logicalType: time-millis |
TIME |
int with logicalType: time-micros |
INT |
long with logicalType: timestamp-millis |
TIMESTAMP |
long with logicalType: timestamp-micros |
BIGINT |
bytes with logicalType: decimal |
DECIMAL |
fixed with logicalType: decimal |
DECIMAL |
null |
NULL (unsupported yet) |
Avro使用Joda-Time表示特定记录类中的逻辑日期和时间类型。 Joda-Time依赖性不属于Flink的发行版。 因此,在运行时,请确保Joda-Time和特定的记录类在您的类路径中。 通过模式字符串指定的Avro格式不需要显示Joda-Time。
确保添加Apache Avro依赖项。
旧的CSV格式
注意:仅用于原型制作!
旧的CSV格式允许使用文件系统连接器读取和写入以逗号分隔的行。
此格式描述了Flink的非标准CSV表源/接收器。 将来,该格式将被适当的RFC兼容版本取代。 写入Kafka时,请使用符合RFC的CSV格式。 现在,将旧版本用于流/批处理文件系统操作。
.withFormat( new OldCsv() .field("field1", Types.STRING) // required: ordered format fields .field("field2", Types.TIMESTAMP) .fieldDelimiter(",") // optional: string delimiter "," by default .lineDelimiter("\n") // optional: string delimiter "\n" by default .quoteCharacter('"') // optional: single character for string values, empty by default .commentPrefix('#') // optional: string to indicate comments, empty by default .ignoreFirstLine() // optional: ignore the first line, by default it is not skipped .ignoreParseErrors() // optional: skip records with parse error instead of failing by default )
旧的CSV格式包含在Flink中,不需要其他依赖项。
注意:目前,用于写入行的旧CSV格式受到限制。 仅支持将自定义字段定界符作为可选参数。
更多TableSources和TableSinks
下表的源和接收器尚未迁移(或尚未完全迁移)到新的统一接口。
这些是Flink随附的其他TableSources:
Class name | Maven dependency | Batch? | Streaming? | Description |
OrcTableSource |
flink-orc |
Y | N | A TableSource for ORC files. |
These are the additional TableSink
s which are provided with Flink:
Class name | Maven dependency | Batch? | Streaming? | Description |
CsvTableSink |
flink-table |
Y | Append | A simple sink for CSV files. |
JDBCAppendTableSink |
flink-jdbc |
Y | Append | Writes a Table to a JDBC table. |
CassandraAppendTableSink |
flink-connector-cassandra |
N | Append | Writes a Table to a Cassandra table. |
OrcTableSource
OrcTableSource读取ORC文件。 ORC是用于结构化数据的文件格式,并以压缩的列表示形式存储数据。 ORC具有很高的存储效率,并支持投影和滤镜下推。
创建一个OrcTableSource,如下所示:
// create Hadoop Configuration Configuration config = new Configuration(); OrcTableSource orcTableSource = OrcTableSource.builder() // path to ORC file(s). NOTE: By default, directories are recursively scanned. .path("file:///path/to/data") // schema of ORC files .forOrcSchema("struct<name:string,addresses:array<struct<street:string,zip:smallint>>>") // Hadoop configuration .withConfiguration(config) // build OrcTableSource .build();
注意:OrcTableSource还不支持ORC的联合类型。
CsvTableSink
CsvTableSink发出一个表到一个或多个CSV文件。
接收器仅支持 append-only 流表。 它不能用于发出连续更新的表。 有关详细信息,请参见表到流转换的文档。 发出流表时,行至少写入一次(如果启用了检查点),并且CsvTableSink不会将输出文件拆分为存储区文件,而是连续写入相同的文件。
CsvTableSink sink = new CsvTableSink( path, // output path "|", // optional: delimit files by '|' 1, // optional: write to a single file WriteMode.OVERWRITE); // optional: override existing files tableEnv.registerTableSink( "csvOutputTable", // specify table schema new String[]{"f0", "f1"}, new TypeInformation[]{Types.STRING, Types.INT}, sink); Table table = ... table.insertInto("csvOutputTable");
JDBCAppendTableSink
JDBCAppendTableSink发出到JDBC连接的表。 接收器仅支持 append-only 流表。 它不能用于发出连续更新的表。 有关详细信息,请参见表到流转换的文档。
JDBCAppendTableSink将每个Table行至少插入一次到数据库表中(如果启用了检查点)。 但是,您可以使用REPLACE或INSERT OVERWRITE指定插入查询,以执行对数据库的向上写入。
要使用JDBC接收器,必须将JDBC连接器依赖项(flink-jdbc)添加到项目中。 然后,您可以使用JDBCAppendSinkBuilder创建接收器:
JDBCAppendTableSink sink = JDBCAppendTableSink.builder() .setDrivername("org.apache.derby.jdbc.EmbeddedDriver") .setDBUrl("jdbc:derby:memory:ebookshop") .setQuery("INSERT INTO books (id) VALUES (?)") .setParameterTypes(INT_TYPE_INFO) .build(); tableEnv.registerTableSink( "jdbcOutputTable", // specify table schema new String[]{"id"}, new TypeInformation[]{Types.INT}, sink); Table table = ... table.insertInto("jdbcOutputTable");
与使用JDBCOutputFormat相似,您必须显式指定JDBC驱动程序的名称,JDBC URL,要执行的查询以及JDBC表的字段类型。
CassandraAppendTableSink
CassandraAppendTableSink向Cassandra表发出一个表。 接收器仅支持append 流表。 它不能用于发出连续更新的表。 有关详细信息,请参见表到流转换的文档。
如果启用了检查点,则CassandraAppendTableSink将所有行至少插入一次到Cassandra表中。 但是,您可以将查询指定为upsert查询。
要使用CassandraAppendTableSink,必须将Cassandra连接器依赖项(flink-connector-cassandra)添加到项目中。 以下示例显示了如何使用CassandraAppendTableSink。
ClusterBuilder builder = ... // configure Cassandra cluster connection CassandraAppendTableSink sink = new CassandraAppendTableSink( builder, // the query must match the schema of the table "INSERT INTO flink.myTable (id, name, value) VALUES (?, ?, ?)"); tableEnv.registerTableSink( "cassandraOutputTable", // specify table schema new String[]{"id", "name", "value"}, new TypeInformation[]{Types.INT, Types.STRING, Types.DOUBLE}, sink); Table table = ... table.insertInto(cassandraOutputTable);
欢迎关注Flink菜鸟公众号,会不定期更新Flink(开发技术)相关的推文