接上篇:https://developer.aliyun.com/article/1622631?spm=a2c6h.13148508.setting.25.27ab4f0ehhuqRu
分析内容
queryExecution 就是对整个执行计划的执行引擎,里面有执行过程中各个中间过程变量,整个执行流程如下:
刚才的例子中的SQL语句经过Parser解析后就会变成一个抽象语法树,对应解析后的逻辑计划AST为:
== Analyzed Logical Plan == total_score: bigint, name: string Aggregate [name#8], [sum(cast(v#26 as bigint)) AS total_score#27L, name#8] +- SubqueryAlias `tmp` +- Project [id#7, ((100 + 10) + score#22) AS v#26, name#8] +- Filter (age#9 >= 11) +- Join Inner, (id#7 = id#20) :- SubqueryAlias `stu` : +- Project [_1#3 AS id#7, _2#4 AS name#8, _3#5 AS age#9] : +- LocalRelation [_1#3, _2#4, _3#5] +- SubqueryAlias `score` +- Project [_1#16 AS id#20, _2#17 AS subject#21, _3#18 AS score#22] +- LocalRelation [_1#16, _2#17, _3#18]
在执行计划中 Project/Projection 代表的意思是投影
其中过滤条件变为了 Filter 节点,这个节点是 UnaryNode (一元节点)类型,只有一个孩子。
两个表中的数据变为了 UnresolvedRelation 节点,节点类型为 LeafNode,即叶子节点,Join操作为节点,这个是一个BinaryNode节点,有两个孩子。
以上节点都是LogicalPlan类型的,可以理解为各种操作的Operator,SparkSQL对各种操作定义了各种Operator。
这些 Operator 组成的语法树就是整个 Catatyst 优化的基础,Catatyst优化器会在这个树上进行分析修改,把树上的节点挪来挪去进行优化。
经过Parser有了抽象语法树,但是并不知道Score,Sum这些东西,所以就需要 Analyer 定位。
Analyzer会把AST上所有Unresolved的东西都转换为Resolved状态,SparkSQL有很多Resolve规则:
ResolverRelations:解析表(列)的基本类型信息
ResolveFunctions:解析出来函数的基本信息
ResolveReferences:解析引用,通常是解析列名
常见优化逻辑
这里用到的优化有:谓词下推(Push Down Predicate)、常量折叠(Constant Folding)、字段裁剪(Columning Pruning):
做完逻辑优化,还需要先转换为物理执行计划,将逻辑上可行的执行计划变为Spark可以真正执行的计划:
SparkSQL 把逻辑节点转换为了相应的物理节点,比如Join算子,Spark根据不同的场景为该算子制定了不同的算法策略。
数据在一个一个的Plan中流转,然后每个plan里面表达式都会对数据进行处理,就相当于经过了一个个小函数的调用处理,这里面有大量的函数调用开销,可以把这些小函数内联一下,当成一个大函数。可以看到最终执行计划每个节点面前有个*号,说明整段代码生成被启用。