Flink 类型机制 及 Stream API和Table API类型推断和转换

注:本文使用flink 版本是0.13

一、类型体系

Flink 有两大API (1)stream API 和 (2)Table API ,分别对应TypeInformation 和 DataType类型体系。

1.1 TypeInformation系统

TypeInformation系统是使用Stream一定会用到的。TypeInformation 以下简称TypeInfo。

TypeInfo 本质就是一对一的类型映射。在java中一个typeInfo就对应着一个确定的java类型。所以在stream api 中某些情况下。给定数据flink可以根据数据自动推断出TypeInfo。

但现在Table API大行其道,Flink已经有意在用DataType替代TypeInfo了。所以Flink中有 DataType To TypeInfo 的API(org.apache.flink.table.runtime.typeutils.InternalTypeInfo虽然只能将DataType转为Table API所规定的TypeInfo),却没有提供 TypeInfo To DataType 的API。

在Stream API中默认使用的承载行数据的类型是org.apache.flink.types.Row

注意:创建流后如果是复杂类型,比Row类型,非标准的Pojo类型,必须明确告诉Flink是审美类型,Flink无法自动推断出的。

(1)可以在创建流时候提供如org.apache.flink.streaming.api.environment.StreamExecutionEnvironment#addSource(org.apache.flink.streaming.api.functions.source.SourceFunction<OUT>, java.lang.String, org.apache.flink.api.common.typeinfo.TypeInformation<OUT>)

(2) 可以在使用function时候提供,如org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator#returns(org.apache.flink.api.common.typeinfo.TypeInformation<T>)

其中return方法参数也有三种(非常重要):

java 复制代码
// 比如 DataStream<Integer> myInts = env.fromElements(1, 2, 3, 4, 5); 提供 Integer.class就可以了。当然不提供FLink也会推断出的。
public SingleOutputStreamOperator<T> returns(Class<T> typeClass);
// 用于创安含有泛型的TypeInfo
// 如:TypeInformation<Tuple2<String, Double>> info = TypeInformation.of(new TypeHint<Tuple2<String, Double>>(){});
public SingleOutputStreamOperator<T> returns(TypeHint<T> typeHint);
// TypeInfo的创建方式
// 1. 基础类型从org.apache.flink.api.common.typeinfo.BasicTypeInfo选择即可。
// 2. 不含泛型的类型可从org.apache.flink.api.common.typeinfo.TypeInformation#of方法创建。
// 3. DataStream<Row> 可由 org.apache.flink.api.java.typeutils.RowTypeInfo 创建
// 4. DataStream<RowData> 只能由 org.apache.flink.table.runtime.typeutils.InternalTypeInfo#of(org.apache.flink.table.types.logical.RowType)等方法创建。注意创建出来字段的Field TypeInfo的也都是Flink Table API预先指定好的。
public SingleOutputStreamOperator<T> returns(TypeInformation<T> typeInfo) ;

(3)可以实现接口org.apache.flink.api.java.typeutils.ResultTypeQueryable提供。

1.2 DataType系统

Table API 中所有的类型都是围绕着DataType构建的。如org.apache.flink.table.api.Schemaorg.apache.flink.table.catalog.ResolvedSchema

前者代表Table api中的Table对象的表结构,后者代表从catalog中获取的表结构。

DataType本质由两部分组成:

java 复制代码
    protected final LogicalType logicalType;
    protected final Class<?> conversionClass;

logicalType 即逻辑类型,是和数据库中的类型对应的。

conversionClass 即运行时java类型,是实际承载数据的类型。

可以说DataType 与物理类型也是一对一的关系,并有conversionClass确定。

举个例子:

DataType与数据库中日期对应的类型是DateType ,它有一个支持conversionClass 的列表。分别支持 Date , LocalDate ,Integer。其中 如果使用Integer作为实际承载数据的类型,此时存储的值是与1970-01-01的天数差值。

java 复制代码
@PublicEvolving
public final class DateType extends LogicalType {
    private static final String FORMAT = "DATE";
    private static final Set<String> NULL_OUTPUT_CONVERSION = conversionSet(new String[]{Date.class.getName(), LocalDate.class.getName(), Integer.class.getName()});
    private static final Class<?> DEFAULT_CONVERSION;
 }

如果需要创建DataType,Flink提供的入口是org.apache.flink.table.api.DataTypes 类,提供了各种DataType类型的创建方法。需要注意的是创建的类型都使用了默认 conversionClass ,其就是LogicalType 实现类中指定的DEFAULT_CONVERSION。如果需要指定运行时的类型就需要使用org.apache.flink.table.types.AbstractDataType#bridgedTo方法。

而在Table API中默认使用的承载行数据类型是org.apache.flink.table.data.RowData,是一个接口。常用的实现类是org.apache.flink.table.data.GenericRowData

在Table API中实际承载数据的类型必须使用FLink指定的类型。比如Date类型必须使用Integer,而不能使用java的LocalDate类型。具体类型可以从方法 org.apache.flink.table.types.utils.DataTypeUtils#toInternalDataType(org.apache.flink.table.types.DataType) 得出。

1.3 Stream API 和 Table API相互转换中的类型

转换为核心是 围绕着实际承载数据的类型 即DataType的conversionClass 应为

转换过程均由TableAPI的核心org.apache.flink.table.api.bridge.java.StreamTableEnvironment完成。

刚刚提及了Schema本质就是DataType,并可由DataType创建org.apache.flink.table.api.Schema.Builder#fromRowDataType 。以下就将Schema代指为DataType了。

如下各个方法中

Stream <--相互转化-->Table 中可以指定DataType,也可以不指定DataType(Schema)。

1.3.1 Stream To Table

1.3.1.1 不指定DataType

Flink会从DataStream的TypeInfo中推断DataType类型。

比如 TypeInfo是 org.apache.flink.api.common.typeinfo.BasicTypeInfo#BYTE_TYPE_INFO 或者TypeInfomation.of(Integer.class) 在创建流时候 是Integer类型,则Flink会自动推断出需要使用DataTypes.INT() 创建的对象并把其实际的承载类conversionClass指定为Integer.class

具体可以参考:flink 类型推断data-type-extraction 章节:

复制代码
# 注意在scala中不要使用primitives 类型需要使用包装类型。因为原始类型不允许为空。
If you intend to implement classes in Scala, it is recommended to use boxed types (e.g. java.lang.Integer) instead of Scala's primitives. Scala's primitives (e.g. Int or Double) are compiled to JVM primitives (e.g. int/double) and result in NOT NULL semantics
# 对于没有被列举的类型,是需要额外提供类型的。比如使用@DataTypeHint
Other JVM bridging classes mentioned in this document require a @DataTypeHint annotation.

下图是官方的java类型推断成为FLink DataType的类型

1.3.1.2 指定DataType

DataType是有LogicalType的,指定了DataType也就指定了LogicalType逻辑类型。

比如现在Row中有一个字段的TypeInfo还是 org.apache.flink.api.common.typeinfo.BasicTypeInfo#BYTE_TYPE_INFO 或者TypeInfomation.of(Integer.class) 也就是java中的int 或 Integer。但指定DataType时候使用DataTypes.DATE().bridgedTo(Integer.class)。此时就已经告诉Flink这里我虽然给你提供的是Integer,但实际代表的逻辑是Date日期类型数据了。以后就可以使用Table API所有关于Date日期的转换方法了。

1.3.2 Table To Stream

Table中都是包含DataType的,可从方法获得,如DataType dt = tbl.getResolvedSchema().toPhysicalRowDataType();

1.3.2.1 不指定DataType

不指定情况比较简单Flink Table API 每种LogicalType逻辑类型都有默认的java类型。

如:

TableDataType JavaType(conversionClass) 代表内容
int Integer.class
bigint Long.class
date Integer 与1900-01-01天数差
time Integer.class 当天的毫秒数

所以不指定情况下,得到的DataStream中的原Date日期类型的数据一定会转为Integer.class 。并不是java中常用的 LocalDate.class ,也不是 LogicalType.DEFAULT_CONVERSION所规定的DEFAULT_CONVERSION = LocalDate.class;

1.3.2.2 指定DataType

指定情况就会在DataStream中获得想要的java类型了。Flink会在此过程中给提供类型转换服务。

如在表中能获取到DataType,如 DataType dt = tbl.getResolvedSchema().toPhysicalRowDataType();

但在toDataStream或toChangelogStream 可以提供DataType。

当两个DataType不一样的时候Flink就会将Table中的java(物理类型)转换为 提供的。

如下流程:

(1)使用 DataStream<Integer>

(2)指定DataTypes.DATE().bridgedTo(Integer.class)创建Table。此时table中的java数据类型仍然是Integer.class。

(3)使用Table创建Stream,并指定 DataTypes.DATE().bridgedTo(LocalDate.class) ,此时得到的流DataStream<LocalDate>

借此流程就实现了DataStream<Integer>Stream<LocalDate> 的转化。

java 复制代码
public interface StreamTableEnvironment extends TableEnvironment {

<T> Table fromDataStream(DataStream<T> dataStream);

<T> Table fromDataStream(DataStream<T> dataStream, Schema schema);

Table fromChangelogStream(DataStream<Row> dataStream);

Table fromChangelogStream(DataStream<Row> dataStream, Schema schema);

DataStream<Row> toDataStream(Table table);

<T> DataStream<T> toDataStream(Table table, AbstractDataType<?> targetDataType);
DataStream<Row> toChangelogStream(Table table);

DataStream<Row> toChangelogStream(Table table, Schema targetSchema);

}

总结

综上,Flink中比较容易从DataType得到 TypeInfo 。而从TypeInfo中转化为想要的逻辑类型,八成额外提供DataType(如Integer转Date)。所以,还是建议直接使用Table API中的DataType更加方便。

参考文章:
Flink之数据类型详解
Flink类型系统
聊聊Java类型擦除、Flink中使用Lambda表达式丢失信息和Flink类型暗示机制
聊聊Java泛型类型擦除及Flink类型暗示(type hint)机制
Flink数据类型&&序列化&&序列化器

相关推荐
WLJT1231231232 小时前
生活电器:重构家居体验的产业变革与发展探索
大数据·人工智能·科技·生活
GIS数据转换器3 小时前
城市基础设施安全运行监管平台
大数据·运维·人工智能·物联网·安全·无人机·1024程序员节
搞科研的小刘选手4 小时前
【云计算专题会议】第二届云计算与大数据国际学术会议(ICCBD 2025)
大数据·人工智能·物联网·5g·云计算·6g·智能通信
电商软件开发 小银4 小时前
微信生态新机遇:视频号推客模式助力商家突围
大数据·人工智能·twitter·系统开发·实体店转型·数字化经济·视频号推客模式
毕设源码-赖学姐4 小时前
【开题答辩全过程】以基于Hadoop的电商数据分析系统为例,包含答辩的问题和答案
大数据·hadoop·分布式·1024程序员节
汤姆yu4 小时前
基于python大数据技术的医疗数据分析与研究
大数据·1024程序员节·医疗数据分析·医疗预测
小马哥编程5 小时前
【软考架构】架构风格:RAG知识库是属于软件八大架构风格中的哪一个,黑板架构风格 ?规则系统体系风格?
大数据·计算机网络·架构·1024程序员节
萤丰信息5 小时前
慧园区:科技赋能下的城市空间新范式
大数据·科技·安全·重构·智慧城市·智慧园区
数据科学作家6 小时前
如何入门python机器学习?金融从业人员如何快速学习Python、机器学习?机器学习、数据科学如何进阶成为大神?
大数据·开发语言·人工智能·python·机器学习·数据分析·统计分析
小鹿学程序6 小时前
搭建虚拟机完全分布式(centos)
大数据