1. EXPLAIN 能解决什么问题?
在 Flink Table/SQL 里,EXPLAIN 主要用来:
- 看清楚:SQL 会被解析成什么 逻辑计划(Logical Plan / AST)
- 看明白:优化器做了哪些 算子改写(如 Filter 下推、Calc 合并、Union 等)
- 看关键:最终落到哪些 物理算子(Exchange、GroupAggregate、TableSourceScan...)
- 看风险:从 1.17 开始可用
PLAN_ADVICE自动提示 潜在风险/优化建议 - 看语义:
CHANGELOG_MODE帮你判断某个节点输出的是不是 insert-only、是否有更新/删除
一句话:先 EXPLAIN,再调参;先看计划,再喷优化器。
2. 两种常用姿势:explainSql vs EXPLAIN PLAN FOR
你在 Java 里一般有两种方式:
方式 A:tEnv.explainSql(sql)
优点:直接返回 String,适合日志输出、调试打印、写单测断言。
方式 B:tEnv.executeSql("EXPLAIN PLAN FOR ...")
优点:更贴近 SQL CLI 的写法,输出也更统一;还能扩展 explain detail。
当你需要"加料"(成本、changelog、建议、json)时,用方式 B 更顺手:
sql
EXPLAIN ESTIMATED_COST, CHANGELOG_MODE, PLAN_ADVICE, JSON_EXECUTION_PLAN
SELECT ...
3. EXPLAIN 输出到底怎么看?(三层结构拆解)
你会看到类似三块内容:
3.1 Abstract Syntax Tree(逻辑层:你写的 SQL 变成了什么)
常见节点:
LogicalTableScan:扫表LogicalFilter:where 条件LogicalProject:选字段/表达式LogicalUnion:UNION ALLLogicalAggregate:GROUP BY + 聚合
逻辑层的价值:确认语义是否按你预期被解析(比如条件是不是在 union 前生效、group by 维度是不是写对了)。
3.2 Optimized Physical Plan(物理层:优化器怎么把它"变快")
常见节点:
Calc(...):常见的"投影 + 过滤"融合体(Filter/Project 合并)Exchange(distribution=[hash[key]]):数据重分区/Shuffle(极其关键!)GroupAggregate:聚合算子TableSourceScan:真正的 source 扫描
物理层的价值:定位性能瓶颈
看到 Exchange(hash[xxx]),你就该条件反射:
"我这里发生了 shuffle,数据会按 key 重分发,会不会倾斜?状态会不会变大?网络会不会爆?"
3.3 Optimized Execution Plan(执行层:更贴近运行时)
通常和物理计划类似,但会更"贴近执行"。
价值:更像最终要跑的图,适合对照 Web UI / JobGraph 心里有数。
4. ExplainDetail:把 EXPLAIN 变成"性能调优报告"
4.1 ESTIMATED_COST:把"感觉慢"变成"成本证据"
它会在节点上附上类似:
- rows
- cpu
- io
- network
- memory
虽然是估算,但非常适合:
- 对比两种 SQL 改写哪个更合理
- 判断某个 scan/聚合是否预期会"吞吐巨大"
4.2 CHANGELOG_MODE:确认动态表语义,避免"写 sink 写错"
示例:changelogMode=[I,UA,D] 代表可能有:
IInsertUAUpdate AfterDDelete
(具体含义以动态表 changelog 定义为准)
它特别适合排查这类问题:
- 你以为是 append-only,结果有更新
- sink 只支持 insert-only,结果上游产生了 update/delete(那就会炸)
4.3 PLAN_ADVICE:让优化器"开口说人话"
从 Flink 1.17 开始,你可以让优化器直接提示:
- 数据倾斜风险(常见在 GroupAgg)
- 非确定性更新 NDU 风险(影响正确性)
- 以及可能的调参建议(例如 mini-batch、聚合策略等)
典型建议(示例中给出的配置):
sql
SET 'table.exec.mini-batch.enabled' = 'true';
SET 'table.exec.mini-batch.allow-latency' = '5s';
SET 'table.exec.mini-batch.size' = '200';
SET 'table.optimizer.agg-phase-strategy' = 'ONE_PHASE';
理解要点:
- mini-batch 常用于提升聚合吞吐、减少频繁更新带来的开销(但会引入延迟窗口)
- agg-phase-strategy 会影响是否走本地-全局两阶段聚合等策略(具体策略以你版本实现为准)
如果你看到 NO_AVAILABLE ADVICE:别失望,至少说明优化器没识别到明显风险点。
4.4 JSON_EXECUTION_PLAN:适合做自动化分析/平台化
拿到 JSON 执行计划后,你可以:
- 写平台把计划存档(SQL → Plan 版本对比)
- 做"规则扫描"(比如出现 Exchange 就报警)
- 辅助生成可视化图
5. 语法一览:查询、INSERT、以及 STATEMENT SET
基本语法:
sql
EXPLAIN [([ExplainDetail[, ExplainDetail]*]) | PLAN FOR] <query_or_insert_or_statement_set>
如果你是多条 insert 一起提交(Statement Set):
sql
STATEMENT SET
BEGIN
INSERT INTO sink1 SELECT ...
INSERT INTO sink2 SELECT ...
END;
你同样可以对 statement set 做 explain,用来提前看到整套写入的计划结构(特别适合平台上"一次提交多写入"的场景)。
6. 实战建议:一套"看计划就能定位"的心法
- 看到 Exchange(hash[key]):先想数据倾斜、网络、状态、并行度
- 看到 GroupAggregate:想"能不能两阶段聚合/mini-batch/局部聚合"
- 看到 changelogMode 非 insert-only:检查 sink 能不能接住 update/delete
- 怀疑性能问题:先加
ESTIMATED_COST - 怀疑正确性风险:加
PLAN_ADVICE看有没有 WARNING(尤其是 NDU)
7. 总结
EXPLAIN 是 Flink SQL 调试与调优的"第一生产力工具":
- 用
explainSql()或EXPLAIN PLAN FOR看计划 - 用
ESTIMATED_COST / CHANGELOG_MODE / PLAN_ADVICE / JSON_EXECUTION_PLAN看细节 - 通过计划中的
Exchange / GroupAggregate / changelogMode快速定位性能与语义风险