1. 什么是Table API & SQL
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中,指定的查询都具有相同的语义,得到相同的结果。
2. 批处理案例实现
-
实现说明
以批处理方式,加载自定义数据,并注册为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(); }
3. 流处理案例实现
-
实现说明
采用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();
4. Flink 1.9 Table架构
在新的架构中,有两个查询处理器:
-
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完全没有关系了。
5. Flink Planner 与 Blink Planner的差异
从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,可以在作业中显式指定。
6. Flink Planner 用法
-
导入依赖
<!-- 表程序计划程序和运行时。这是1.9版本之前Flink的唯一计划者。仍然是推荐的。--> <dependency> <groupId>org.apache.flink</groupId> <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);
7. Blink Planner 用法
-
导入依赖
<!-- 使用blink执行计划的时候需要导入这个包--> <dependency> <groupId>org.apache.flink</groupId> <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 相关的依赖打包,不需要再次引入,避免冲突。
共同学习,写下你的评论
评论加载中...
作者其他优质文章