感谢您的小爱心(关注 + 点赞 + 再看),对博主的肯定,会督促博主持续的输出更多的优质实战内容!!!
1.序篇-本文结构
大数据羊说
用数据提升美好事物发生的概率~
32篇原创内容
公众号
本文主要介绍 flink sql 与 calcite 之间的关系。flink sql 的解析主要依赖 calcite。
而博主通过此文抛砖引玉帮助大家理解 flink sql 在解析中是怎样依赖 calcite 的,以及 flink sql 解析的流程,sql parser 相关内容。希望对大家有所帮助。
本文通过以下几节进行介绍,对某个章节感兴趣的可以直接划到对应章节。
- 背景篇-一条 flink sql 的执行过程
-
发挥自己的想象力
-
看看 flink 的实现
- 简介篇-calcite 扮演的角色
-
calcite 是啥?
-
flink sql 为啥选择 calcite?
- 案例篇-calcite 的能力、案例
-
先用用 calcite
-
关系代数
-
calcite 必知的基础 model
-
calcite 的处理流程(以 flink sql 为例)
-
calcite 怎么做到这么通用?
- 原理剖析篇-calcite 在 flink sql 中大展身手
-
FlinkSqlParserImpl
-
FlinkSqlParserImpl 的生成
- 总结与展望篇
2.背景篇-一条 flink sql 的执行过程
本节先给大家大致描述一条 flink sql 的执行过程
,不了解详细内容不要紧,主要先了解整个流程,有了全局视角之后,后续会详述细节。
在介绍一条 flink sql 的执行过程之前,先来看看 flink datastream 任务的执行过程,这对理解一条 flink sql 的执行过程有很大的帮助。
-
datastream:datastream 在使用时要在 flink datastream api 提供的各种 udf(比如 flatmap,keyedprocessfunction 等)中自定义处理逻辑,具体的业务执行逻辑都是敲代码、 java 文件写的,然后编译在 jvm 中执行,就和一个普通的 main 函数应用一模一样的流程。因为代码执行逻辑都是自己写的,所以这一部分相对好理解。
-
sql:java 编译器不能识别和编译一条 sql 进行执行,那么一条 SQL 是咋执行的呢?
2.1.先发挥自己的想象力
我们逆向思维进行考虑,如果想让一条 flink sql 按照我们的预期在 jvm 中执行,需要哪些过程。
-
整体来说:参考 datastream,如果 jvm 能执行 datastream java code 编译后的 class 文件,那么加一个 sql 解析层,能将 sql 逻辑解析为 datastream 的各种算子,然后编译执行不就 vans 了。
-
sql parser:首先得有一个 sql parser 吧,得先能识别 sql 语法,将 sql 语法转化为 AST、具体的关系代数。
-
关系代数到 datastream 算子的映射:sql 逻辑解析为 datastream,需要有一个解析的映射逻辑吧。sql 是基于关系代数的,可以维护一个 sql 中的每个关系代数到具体 datastream 接口的映射关系,有了这些映射关系我们就可以将 sql 映射成一段可执行的 datastream 代码。举个例子:其可以将:
-
sql select xxx 解析为类似 datastream 中的 map
-
where xxx 解析为 filter
-
group by 解析成 keyby
-
sum(xx),count(xxx)可以解析为 datastream 中的 aggregate function
-
etc…
-
代码生成:有了 sql AST,sql 到 datasretam 算子的映射关系之后,就要进行具体的代码生成了。比如去解析 sql AST 中具体哪些字段用作 where 逻辑,哪些字段用作 group by,都需要生成对应具体的 datastream 代码。
-
运行:经过上述流程之后,就可以将一个 sql 翻译成一个 datastream 作业了,happy 的执行。
如下图所示,描绘了上述逻辑:
12
那么这个和 flink 实际实现有啥异同呢?
flink 大致是这样做的,虽在 flink 本身的中间还有一些其他的流程,后来的版本也不是基于 datastream,但是整体的处理逻辑还是和上述一致的。
所以不了解整体流程的同学可以先按照上述流程进行理解。
按照 博主的脑洞
来总结一条 sql 的使命就是:sql -> AST -> codegen(java code) -> 让我们 run 起来好吗
2.2.看看 flink 的实现
26
上面手绘可能看不清,下面这张图更清楚。
28
标准的一条 flink sql 运行起来的流程如下:
Notes:刚开始对其中的 SqlNode,RelNode 概念可能比较模糊。先理解整个流程,后续会详细介绍这些概念。
-
sql 解析阶段:calcite parser 解析(sql -> AST,AST 即 SqlNode Tree)
-
SqlNode 验证阶段:calcite validator 校验(SqlNode -> SqlNode,语法、表达式、表信息)
-
语义分析阶段:SqlNode 转换为 RelNode,RelNode 即 Logical Plan(SqlNode -> RelNode)
-
优化阶段:calcite optimizer 优化(RelNode -> RelNode,剪枝、谓词下推等)
-
物理计划生成阶段:Logical Plan 转换为 Physical Plan(等同于 RelNode 转换成 DataSet\DataStream API)
-
后续的运行逻辑与 datastream 一致
可以发现 flink 的实现
比 博主的脑洞
整体主要框架上面是一致的。多出来的部分主要是 SqlNode 验证阶段,优化阶段。
3.简介篇-calcite 在 flink sql 中的角色
大致了解了 一条 flink sql 的运行流程
之后,我们来看看 calcite 这玩意到底在 flink 里干了些啥。
根据上文总结来说 calcite 在 flink sql 中担当了 sql 解析、验证、优化
功能。
30
看着 calcite 干了这么多事,那 calcite 是个啥东东,它的定位是啥?
3.1.calcite 是啥?
calcite 是一个动态数据的管理框架,它可以用来构建数据库系统的不同的解析的模块,但是它不包含数据存储数据处理等功能。
calcite 的目标是一种方案,适应所有的需求场景,希望能为不同计算平台和数据源提供统一的 sql 解析引擎,但是它只是提供查询引擎,而没有真正的去存储这些数据。
61
下图是目前使用了 calcite 能力的其他组件,也可见官网 https://calcite.apache.org/docs/powered_by.html 。
4
简单来说的话,可以先理解为 calcite 具有这几个功能(当然还有其他很牛逼的功能,感兴趣可以自查官网)。
-
自定义 sql 解析器:比如说我们新发明了一个引擎,然后我们要在这个引擎上来创造一套基于 sql 的接口,那么我们就可以使用直接 calcite,不用自己去写一套专门的 sql 的解析器,以及执行以及优化引擎,calcite 人都有。
-
sql parser(extends SqlAbstractParserImpl):将 sql 的各种关系代数解析为具体的 AST,这些 AST 都能对应到具体的 java model,在 java 的世界里面,对象很重要,有了这些对象(
SqlSelect
、SqlNode
),就可以根据这些对象做具体逻辑处理了。举个例子,如下图,一条简单的select c,d from source where a = '6'
sql,经过 calcite 的解析之后,就可以得到 AST model(SqlNode)。可以看到有SqlSelect
、SqlIdentifier
、SqlIdentifier
、SqlCharStringLiteral
。 -
sql validator(extends SqlValidatorImpl):根据语法、表达式、表信息进行 SqlNode 正确性校验。
-
sql optimizer:剪枝、谓词下推等优化
上面的这些能力整体组成如下图所示: