flinksql的执行流程
Flink SQL 的执行流程是一个将声明式的 SQL 文本逐步翻译、优化,最终转化为 Flink 集群上可运行的分布式数据流图(JobGraph)的过程。
这个过程深度依赖了 Apache Calcite(一个开源的 SQL 解析、校验和优化框架)。
以下是 Flink SQL 执行的核心流程,主要分为 8 个阶段:
Flink SQL 执行流程全景图
plaintext
[SQL 文本]
│
▼ (Step 1: Parser 解析)
[AST / SqlNode 树]
│
▼ (Step 2: Validator 校验 - 结合 Catalog)
[验证后的 SqlNode 树]
│
▼ (Step 3: Converter 转换)
[逻辑计划 / RelNode]
│
▼ (Step 4: Optimizer 优化 - Calcite RBO/CBO)
[优化后的逻辑计划 / Flink Physical Plan]
│
▼ (Step 5: Translator 翻译)
[执行图 / ExecNode Plan]
│
▼ (Step 6: Code Generation 代码生成)
[Transformation API (DataStream)]
│
▼ (Step 7: Client Compiler 客户端编译)
[StreamGraph -> JobGraph]
│
▼ (Step 8: Cluster Execution 集群运行)
[ExecutionGraph -> 物理执行]
详细步骤解析
1. SQL 解析 (SQL Parsing)
- 输入:SQL 字符串(例如
SELECT user, COUNT(*) FROM Orders GROUP BY user)。 - 工具:Apache Calcite 的 Parser。
- 工作:进行词法分析和语法分析,检查 SQL 是否符合语法规则(如括号是否配对,关键字是否拼写正确)。
- 输出:AST(抽象语法树),在 Calcite 中表示为
SqlNode树。
2. SQL 校验 (SQL Validation)
- 输入:
SqlNode树。 - 工具:Calcite Validator 和 Flink Catalog。
- 工作:进行语义检查。
- 通过访问 Catalog(元数据)检查表名、字段名、函数名是否存在。
- 检查字段类型是否匹配(例如不能把 String 类型赋值给 Int)。
- 进行隐式类型转换。
- 输出:验证后的
SqlNode树。
3. 逻辑计划生成 (Logical Plan Generation)
- 输入:验证后的
SqlNode树。 - 工具:Calcite
SqlToRelConverter。 - 工作:将 SQL 语法树转换成关系代数表达式(即逻辑计划)。
- 输出:逻辑代数计划(Logical Plan),在 Calcite 中表示为
RelNode树(例如 LogicalProject, LogicalFilter, LogicalAggregate)。
4. 逻辑计划优化 (Logical Optimization)
这是最核心的一步,Flink 结合了 Calcite 的优化器和自己定制的规则。
- 工具:Calcite Optimizer(包括基于规则的优化 RBO 和基于代价的优化 CBO)。
- 工作:
- RBO(基于规则):应用常用优化规则,如谓词下推(Filter Pushdown)、投影剪裁(Projection Pruning)、常量折叠等。
- CBO(基于代价):估算算子代价(CPU、内存、I/O 等),选择最优的 Join 顺序或 Join 算法(如 HashJoin 还是 SortMergeJoin)。
- 流/批特有优化:如果是流式 SQL,会引入状态(State)清理、Retraction(撤回流)处理机制;如果是批式 SQL,会进行算子链合并等。
- 输出:物理计划(Flink Physical Plan)。
5. 执行节点翻译 (Execution Plan Generation)
- 工作:将物理计划(Physical Plan)翻译成 Flink 的内部执行图——
ExecNode物理计划。 - 特点:
ExecNode承载了如何将算子翻译成具体的 DataStream 算子的信息。它会考虑流处理中的 Changelog 模式(Insert/Update/Delete 的传递)。 - 输出:
ExecNodeDAG 图。
6. 代码生成与 Transformation 转换 (Code Generation & Translation)
- 工具:Janino 编译器(Java 代码生成技术)。
- 工作:
- Flink 为了追求极致性能,避免虚函数调用和装箱拆箱开销,会使用 Codegen(代码生成) 技术,动态生成 Java 字节码(例如生成一个专门处理特定 Schema 的 MapFunction 类)。
- 将
ExecNode翻译成 Flink DataStream API 底层的Transformation列表。
- 输出:
List<Transformation>(这是 DataStream API 的底层底座)。
7. 客户端生成 JobGraph (JobGraph Generation)
- 工作:
- 将
Transformation列表转换成StreamGraph(表示流拓扑的结构)。 - 在 Client 端,将
StreamGraph优化并转换为JobGraph。这一步会进行算子链(Operator Chaining)合并,减少线程间通信开销。
- 将
- 输出:
JobGraph(这是可以提交给 Flink 运行时集群的最终统一格式)。
8. 集群运行 (Cluster Execution)
- 工作:
- Client 将
JobGraph提交给集群的 JobManager。 - JobManager 将其转化为
ExecutionGraph(并行化版本的 JobGraph)。 - JobManager 向 ResourceManager 申请资源,并将任务分发给 TaskManager 上的 Slot 开始分布式执行。
- Client 将
总结:关键概念对比
| 阶段 | 表现形式 (Representation) | 核心作用 |
|---|---|---|
| SQL Parse | SqlNode (AST) |
语法结构化 |
| Validate | Validated SqlNode |
元数据、类型校验 |
| Logical | RelNode (Logical Plan) |
关系代数表达 |
| Optimize | Flink Physical Plan | 结合流/批特性的优化 |
| Codegen | Transformation / Java Bytecode |
生成高性能执行代码 |
| Runtime | JobGraph / ExecutionGraph |
提交分布式集群执行 |
右滑查看面试常问