Table API& SQL 是一种关系型API,用户可以像操作MySQL数据库表一样的操作数据,而不需要写Java代码完成flink function,更不需要手工的优化Java代码调优。SQL对一个非程序员操作来讲,学习成本很低,如果一个系统提供SQL支持,将很容易被用户接受。
总结来说,关系型API的好处:
关系型API是声明式的
查询能够被有效的优化
查询可以高效的执行
“Everybody” knows SQL
Flink本身是批流统一的处理框架,所以Table API和SQL,就是批流统一的上层处理API。
Table API& SQL 是流处理和批处理统一的API层,如下图:
flink在runtime层是统一的,因为flink将批任务看做流的一种特例来执行
在API层,flink为批和流提供了两套API(DataSet和DataStream)
Table API是一套内嵌在Java和Scala语言中的查询API,它允许我们以非常直观的方式,组合来自一些关系运算符的查询(比如select、filter和join)。
对于Flink SQL,就是直接可以在代码中写SQL,来实现一些查询(Query)操作。Flink的SQL支持,基于实现了SQL标准的Apache Calcite(Apache开源SQL解析工具)。
无论输入是批输入还是流式输入,在这两套API中,指定的查询都具有相同的语义,得到相同的结果。
实现说明
以批处理方式,加载自定义数据,并注册为table表,然后统计每个人名出现的次数,并打印出来。
WordCount jack 2
WordCount mike 1
实现步骤
获取批处理运行环境
获取Table运行环境
加载自定义数据源信息
将外部数据构建成表
使用table方式查询数据
执行任务,打印结果
代码实现
TableApi实现方式:
/**
* Table Api 实现方式
*/
public static void tableApi() throws Exception{
//1. 初始化运行环境
ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
BatchTableEnvironment tEnv = BatchTableEnvironment.create(env);
//2. 编写数据源信息
DataSet<WordCount> input = env.fromElements(
new WordCount("mike", 1),
new WordCount("jack", 1),
new WordCount("jack", 1));
//3. 将dataSet转换成Table对象
Table table = tEnv.fromDataSet(input);
//4. 对word进行分组,然后查询指定的字段
Table filterTable = table
.groupBy("word")
.select("word, frequency.sum as frequency");
//5. 将DataSet转换成Table对象
DataSet<WordCount> result = tEnv.toDataSet(filterTable, WordCount.class);
//6. 打印输出结果
result.print();
}
SQL实现方式:
/**
* Table Api 实现方式
*/
public static void flinkSQL() throws Exception{
//1. 初始化运行环境
ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
BatchTableEnvironment tEnv = BatchTableEnvironment.create(env);
//2. 编写数据源信息
DataSet<WordCount> input = env.fromElements(
new WordCount("mike", 1),
new WordCount("jack", 1),
new WordCount("jack", 1));
//3. 将dataSet转换成Table对象
Table table = tEnv.fromDataSet(input);
//4. 创建临时视图
tEnv.createTemporaryView("WordCount", input, "word, frequency");
//5. 执行sql查询
Table tableSQL = tEnv.sqlQuery(
"SELECT word, SUM(frequency) as frequency FROM WordCount GROUP BY word");
//6. 将DataSet转换成Table对象
DataSet<WordCount> result = tEnv.toDataSet(tableSQL, WordCount.class);
//7. 打印输出结果
result.print();
}
实现说明
采用Flink流式环境, 加载多个集合数据, 转换为Table, 并将Table转换为DataStream,采用SQL方式进行合并处理。
实现步骤
获取流处理环境
设置并行度
获取Table运行环境
加载集合数据
转换DataStream为Table
将DataStream注册成Table
使用union all将两个表进行关联
执行任务,打印输出
代码实现
StreamSqlApplication类:
//1. 初始化流式开发环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
StreamTableEnvironment tEnv = StreamTableEnvironment.create(env);
//2. 创建第一个订单流
DataStream<Order> orderA = env.fromCollection(Arrays.asList(
new Order(1L, "beer", 3),
new Order(1L, "diaper", 4),
new Order(3L, "rubber", 2)));
//3. 创建第二个订单流
DataStream<Order> orderB = env.fromCollection(Arrays.asList(
new Order(2L, "pen", 3),
new Order(2L, "rubber", 3),
new Order(4L, "beer", 1)));
//4. 将DataStream转换成Table(可以将DataStream转换成Table)
Table tableA = tEnv.fromDataStream(orderA, "user, product, amount");
//5. 将DataStream注册成Table(也可以将DataStream注册成Table)
tEnv.registerDataStream("OrderB", orderB, "user, product, amount");
//6. 使用union all将两个表进行关联
Table result = tEnv.sqlQuery("SELECT * FROM " + tableA +
" UNION ALL " +
" SELECT * FROM OrderB ");
//7. 将数据流进行输出
tEnv.toAppendStream(result, Order.class).print().setParallelism(1);
//8. 执行任务
env.execute();
在新的架构中,有两个查询处理器:
Flink Query Processor,也称作Old Planner
Blink Query Processor,也称作Blink Planner
查询处理器是 Planner 的具体实现,通过parser、optimizer、codegen(代码生成技术)等流程将 Table API & SQL作业转换成 Flink Runtime 可识别的 Transformation DAG,最终由 Flink Runtime 进行作业的调度和执行。
Flink 的查询处理器针对流计算和批处理作业有不同的分支处理,流计算作业底层的 API 是 DataStream API, 批处理作业底层的 API 是 DataSet API。
Blink 的查询处理器则实现流批作业接口的统一,底层的 API 都是Transformation,这就意味着我们和Dataset完全没有关系了。
从Flink1.9开始,提供了两种不同的 planner 实现:Blink planner 和 1.9之前旧的 Old Planner。Planner 负责将算子转换为 Flink 可执行的、优化之后的 Flink job。这两个 Planner 拥有不同的优化规则和 runtime 类。
在模型角度,Flink Planner 没有考虑流计算和批处理的统一,在底层会分别转换到到 DataStream API 和 DataSet API 上。而 Blink Planner 将批数据集看作 bounded DataStream (有界流式数据) ,流计算作业和批处理作业最终都会转换到 Transformation API 上。
在架构角度,Blink Planner针对批处理和流计算,分别实现了BatchPlanner 和 StreamPlanner ,两者大部分的代码和优化逻辑都是共用的。 Old Planner 针对批处理和流计算的代码实现的是完全独立的两套体系,基本没有实现代码和优化逻辑复用。
除了模型和架构上的优点外,Blink Planner 在阿里巴巴集团内部的海量业务场景下沉淀了许多实用功能,集中在三个方面:
Blink Planner 对代码生成机制做了改进、对部分算子进行了优化,提供了丰富实用的新功能,如维表 join、Top N、MiniBatch、流式去重、聚合场景的数据倾斜优化等新功能。
Blink Planner 的优化策略是基于公共子图的优化算法,包含了基于成本的优化(CBO)和基于规则的优化(CRO)两种策略,优化更为全面。同时,Blink Planner 支持从 catalog 中获取数据源的统计信息,这对CBO优化非常重要。
Blink Planner 提供了更多的内置函数,更标准的 SQL 支持
整体看来,Blink 查询处理器在架构上更为先进,功能上也更为完善。出于稳定性的考虑,Flink 1.9 默认依然使用 Flink Planner,用户如果需要使用 Blink Planner,可以在作业中显式指定。
导入依赖
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-table-planner_${scala.binary.version}artifactId>
<version>${flink.version}version>
dependency>
代码片段
流式查询:
import org.apache.flink.table.api.EnvironmentSettings;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.table.api.java.StreamTableEnvironment;
// Flink 流式查询
EnvironmentSettings fsSettings = EnvironmentSettings.newInstance().useOldPlanner().inStreamingMode().build();
StreamExecutionEnvironment fsEnv = StreamExecutionEnvironment.getExecutionEnvironment();
StreamTableEnvironment fsTableEnv = StreamTableEnvironment.create(fsEnv, fsSettings);
// or TableEnvironment fsTableEnv = TableEnvironment.create(fsSettings)
批数据查询:
//导入包
import org.apache.flink.api.java.ExecutionEnvironment;
import org.apache.flink.table.api.java.BatchTableEnvironment;
// Flink 批数据查询
ExecutionEnvironment fbEnv = ExecutionEnvironment.getExecutionEnvironment();
BatchTableEnvironment fbTableEnv = BatchTableEnvironment.create(fbEnv);
导入依赖
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-table-planner-blink_${scala.binary.version}artifactId>
<version>${flink.version}version>
dependency>
代码片段
流式查询:
//导入包
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.table.api.EnvironmentSettings;
import org.apache.flink.table.api.TableEnvironment;
import org.apache.flink.table.api.java.StreamTableEnvironment;
// Flink 流式查询
StreamExecutionEnvironment bsEnv = StreamExecutionEnvironment.getExecutionEnvironment();
EnvironmentSettings bsSettings = EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build();
StreamTableEnvironment bsTableEnv = StreamTableEnvironment.create(bsEnv, bsSettings);
// or TableEnvironment bsTableEnv = TableEnvironment.create(bsSettings);
批数据查询:
// Flink 批数据查询
EnvironmentSettings bbSettings = EnvironmentSettings.newInstance().useBlinkPlanner().inBatchMode().build();
TableEnvironment bbTableEnv = TableEnvironment.create(bbSettings);
说明: 如果作业需要运行在集群环境,打包时将 Blink Planner 相关依赖的 scope 设置为 provided,表示这些依赖由集群环境提供。这是因为 Flink 在编译打包时,已经将 Blink Planner 相关的依赖打包,不需要再次引入,避免冲突。
本文由mirson创作分享,如需进一步交流,请加QQ群:19310171或访问www.softart.cn