【Flink】FlinkSQL和Table编程案例

922 阅读5分钟

「这是我参与2022首次更文挑战的第28天,活动详情查看:2022首次更文挑战

一、概述

  • Flink SQL & Table 背景和原理
  • 动态表的概念
  • 常用SQL和内置函数

为什么需要关系型 API

Flink 通过 Table API & SQL 实现了批流统一。

Table APISQL 处于最顶端,是 Flink 提供的高级 API 操作。Flink SQLFlink 实时计算为简化计算模型,降低用户使用实时计算门槛而设计的一套符合标准 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 查询的主要步骤如下:

  1. SQL 解析成未经校验的抽象语法树(ASTAbstract Syntax Tree)。抽象语法树是和语言无关的形式,这和交叉编译的第一步类似。
  2. Validate:验证 AST,主要验证 SQL 语句是否合法,验证后的结果是 RelNode 树。
  3. Optimize: 优化 RelNode 树并生成物理执行计划。
  4. Execute:将物理执行计划转换成特定平台的执行代码,如 FlinkDataStream 的应用程序。

如图:

2020-07-1210:06.png

无论是批查询 SQL 还是流式查询 SQL,都会经过对应的转换器 Parser 转换成为节点树 SQLNode tree,然后生成逻辑执行计划 Logical Plan,逻辑执行计划在经过优化后生成真正可以执行的物理执行计划,交给 DataSet 或者 DataStreamAPI 去执行。

一个完整的 Flink Table & SQL Job 也是由 SourceTransformationSink 构成:

2020-07-1213:31.png

三、动态表

与传统的表 SQL 查询相比,Flink Table & SQL 在处理流数据时会时时刻刻处于动态的数据变化中,所以便有了一个动态表的概念。

动态表的查询与静态表一样,但是,在查询动态表的时候,SQL 会做连续查询,不会终止。

举个栗子,Flink 程序接受一个 Kafka 流作为输入,Kafka 中为用户的购买记录:

2020-07-1213:33.png

首先,Kafka 的消息会被源源不断的解析成一张不断增长的动态表,我们在动态表上执行的 SQL 会不断生成新的动态表作为结果表。

DataStream 上执行 SQL 查询,处理过程如下:

  1. DataStream 转换成动态表
  2. 在动态表上定义持续查询
  3. 将持续查询的实时结果转换成动态表
  4. 将表示查询结果的动态表转换成 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)滚动窗口

滚动窗口的特点是:有固定大小、窗口中的数据不会重叠,如下图所示:

2020-07-1213:36.png

语法结构如下:

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_STARTTUMBLE_END 代表窗口的开始时间和窗口的结束时间,TUMBLE (timeLine, INTERVAL '1' DAY) 中的 timeLine 代表时间字段所在的列,INTERVAL '1' DAY 表示时间间隔为一天。

2)滑动窗口

滑动窗口有固定的大小,与滚动窗口不同的是滑动窗口可以通过 slide 参数控制滑动窗口的创建频率。需要注意的是,多个滑动窗口可能会发生数据重叠,具体语义如下:

2020-07-1213:39.png

滑动窗口的语法与滚动窗口相比,只多了一个 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)会话窗口

会话窗口定义了一个非活动时间,假如在指定的时间间隔内没有出现事件或消息,则会话窗口关闭。

2020-07-1213:41.png

会话窗口的语法如下:

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