「这是我参与2022首次更文挑战的第28天,活动详情查看:2022首次更文挑战」
一、概述
Flink SQL & Table背景和原理- 动态表的概念
- 常用
SQL和内置函数
为什么需要关系型 API ?
Flink通过Table API&SQL实现了批流统一。
Table API和SQL处于最顶端,是Flink提供的高级API操作。Flink SQL是Flink实时计算为简化计算模型,降低用户使用实时计算门槛而设计的一套符合标准SQL语义的开发语言。
依赖:
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-clients_2.11</artifactId>
<version>${flink.version}</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-table-api-java-bridge_2.11</artifactId>
<version>1.11.1</version>
</dependency>
<!-- 需要 scala api 则加 -->
<!--<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-table-api-scala-bridge_2.11</artifactId>
<version>1.11.1</version>
</dependency>-->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-table-planner_${scala.binary.version}</artifactId>
<version>${flink.version}</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-table-planner-blink_2.11</artifactId>
<version>1.11.1</version>
</dependency>
二、原理
在离线计算场景下
Hive几乎扛起了离线数据处理的半壁江山。它的底层对SQL的解析用到了Apache Calcite
Flink 同样把 SQL 的解析、优化和执行教给了 Calcite。
Apache Calcite 执行 SQL 查询的主要步骤如下:
- 将
SQL解析成未经校验的抽象语法树(AST,Abstract Syntax Tree)。抽象语法树是和语言无关的形式,这和交叉编译的第一步类似。 Validate:验证AST,主要验证SQL语句是否合法,验证后的结果是RelNode树。Optimize: 优化RelNode树并生成物理执行计划。Execute:将物理执行计划转换成特定平台的执行代码,如Flink的DataStream的应用程序。
如图:
无论是批查询
SQL还是流式查询SQL,都会经过对应的转换器Parser转换成为节点树SQLNode tree,然后生成逻辑执行计划Logical Plan,逻辑执行计划在经过优化后生成真正可以执行的物理执行计划,交给DataSet或者DataStream的API去执行。
一个完整的 Flink Table & SQL Job 也是由 Source、Transformation、Sink 构成:
三、动态表
与传统的表 SQL 查询相比,Flink Table & SQL 在处理流数据时会时时刻刻处于动态的数据变化中,所以便有了一个动态表的概念。
动态表的查询与静态表一样,但是,在查询动态表的时候,SQL 会做连续查询,不会终止。
举个栗子,Flink 程序接受一个 Kafka 流作为输入,Kafka 中为用户的购买记录:
首先,Kafka 的消息会被源源不断的解析成一张不断增长的动态表,我们在动态表上执行的 SQL 会不断生成新的动态表作为结果表。
在 DataStream 上执行 SQL 查询,处理过程如下:
DataStream转换成动态表- 在动态表上定义持续查询
- 将持续查询的实时结果转换成动态表
- 将表示查询结果的动态表转换成
DataStream,这样应用程序就可以借助DataStream API进一步变换查询结果。
四、Flink Table & SQL 算子和内置函数
简单的例子:
SELECT * FROM Table;
SELECT name,age FROM Table;
SELECT name,age FROM Table where name LIKE '%小明%';
SELECT * FROM Table WHERE age = 20;
SELECT name, age
FROM Table
WHERE name IN (SELECT name FROM Table2)
SELECT name,age FROM Table where name LIKE '%小明%';
SELECT * FROM Table WHERE age = 20;
SELECT name, age
FROM Table
WHERE name IN (SELECT name FROM Table2)
SELECT *
FROM User LEFT JOIN Product ON User.name = Product.buyer
SELECT *
FROM User RIGHT JOIN Product ON User.name = Product.buyer
SELECT *
FROM User FULL OUTER JOIN Product ON User.name = Product.buyer
窗口
根据窗口数据划分的不同,目前 Apache Flink 有如下 3 种:
-
滚动窗口,窗口数据有固定的大小,窗口中的数据不会叠加;
-
滑动窗口,窗口数据有固定大小,并且有生成间隔;
-
会话窗口,窗口数据没有固定的大小,根据用户传入的参数进行划分,窗口数据无叠加;
1)滚动窗口
滚动窗口的特点是:有固定大小、窗口中的数据不会重叠,如下图所示:
语法结构如下:
SELECT
[gk],
[TUMBLE_START(timeCol, size)],
[TUMBLE_END(timeCol, size)],
agg1(col1),
...
aggn(colN)
FROM Tab1
GROUP BY [gk], TUMBLE(timeCol, size)
例如,需要计算每个用户每天的订单数量:
SELECT user, TUMBLE_START(timeLine, INTERVAL '1' DAY) as winStart, SUM(amount)
FROM Orders
GROUP BY TUMBLE(timeLine, INTERVAL '1' DAY), user;
其中,
TUMBLE_START和TUMBLE_END代表窗口的开始时间和窗口的结束时间,TUMBLE (timeLine, INTERVAL '1' DAY)中的timeLine代表时间字段所在的列,INTERVAL '1' DAY表示时间间隔为一天。
2)滑动窗口
滑动窗口有固定的大小,与滚动窗口不同的是滑动窗口可以通过 slide 参数控制滑动窗口的创建频率。需要注意的是,多个滑动窗口可能会发生数据重叠,具体语义如下:
滑动窗口的语法与滚动窗口相比,只多了一个 slide 参数:
SELECT
[gk],
[HOP_START(timeCol, slide, size)] ,
[HOP_END(timeCol, slide, size)],
agg1(col1),
...
aggN(colN)
FROM Tab1
GROUP BY [gk], HOP(timeCol, slide, size)
例如,我们要每间隔一小时计算一次过去 24 小时内每个商品的销量:
SELECT product, SUM(amount)
FROM Orders
GROUP BY HOP(rowtime, INTERVAL '1' HOUR, INTERVAL '1' DAY), product
上述案例中的
INTERVAL '1' HOUR代表滑动窗口生成的时间间隔。
3)会话窗口
会话窗口定义了一个非活动时间,假如在指定的时间间隔内没有出现事件或消息,则会话窗口关闭。
会话窗口的语法如下:
SELECT
[gk],
SESSION_START(timeCol, gap) AS winStart,
SESSION_END(timeCol, gap) AS winEnd,
agg1(col1),
...
aggn(colN)
FROM Tab1
GROUP BY [gk], SESSION(timeCol, gap)
举例,我们需要计算每个用户过去 1 小时内的订单量:
SELECT user, SESSION_START(rowtime, INTERVAL '1' HOUR) AS sStart, SESSION_ROWTIME(rowtime, INTERVAL '1' HOUR) AS sEnd, SUM(amount)
FROM Orders
GROUP BY SESSION(rowtime, INTERVAL '1' HOUR), user