type
status
date
slug
summary
tags
category
icon
password
第五章 SparkSQL 逻辑计划(LogicalPlan)
逻辑计划阶段在整个流程中起着承前启后的作用。在此阶段,字符串形态的 SQL 语句转换为树结构形态的逻辑算子树,SQL 中所包含的各种处理逻辑(过滤、剪裁等)和数据信息都会被整合在逻辑算子树的不同节点中。逻辑计划本质上是一种中间过程表示,与 Spark 平台无关,后续阶段会进一步将其映射为可执行的物理计划。
Spark SQL 语句经过
SparkSqlParser
解析生成 Unresolved LogicalPlan
,到最终优化成为 Optimized LogicalPlan
,这个流程主要经过三个阶段。具体来讲,这个阶段所完成的工作分别如下:
1)由
SparkSqlParser
中的 AstBuilder
执行节点访问,将语法树的各种 Context 节点转换成对应的 LogicalPlan
节点,从而成为一棵未解析的逻辑算子树 Unresolved LogicalPlan
,此时的逻辑算子树是最初形态,不包含数据信息与列信息等。
2)由 Analyzer
系列的规则作用在 Unresolved LogicalPlan
上,对树上的节点绑定各种数据信息,生成解析后的逻辑算子树 Analyzed LogicalPlan
3)由 Spark SQL 中的优化器 Optimizer
将一系列优化规则作用到上一步生成的逻辑算子树中,在确保结果正确的前提下改写其中的低效结构,生成优化后的逻辑算子树 Optimized LogicalPlan
。以下部分参考其他博客,原书讲解实在是太复杂了。
AstBuilder 机制:生成 Unresolved LogicalPlan
我们先看看
AstBuilder
中的代码:代码中会先判断是否有 FROM 子语句,有的话会去生成对应的
Logical Plan
,再调用 withQuerySpecification()
方法,而 withQuerySpecification()
方法是核心的方法。它会处理包括 SELECT,FILTER,GROUP BY,HAVING 等子语句的逻辑。使用 scala 的模式匹配,匹配不同的子语句生成不同的 Logical Plan
。LogicalPlan
继承自 TreeNode
,所以本质上 LogicalPlan
就是一棵树。而实际上,
LogicalPlan
还有多个子类,分别表示不同的 SQL 子语句。- LeafNode,叶子节点,一般用来表示用户命令
- UnaryNode,一元节点,表示 FILTER 等操作
- BinaryNode,二元节点,表示 JOIN,GROUP BY等操作
看看一个测试案例,简单生成一个临时的 VIEW,然后直接 SELECT 查询这个 VIEW。代码如下:
最终经过 parse SQL 后会变成如下的内容:
这个 Project 是 UnaryNode 的一个子类(SELECT 是一元节点),表明我们要查询的字段是 key。
通过 SQL parse 生成的这棵树,叫
Unresolved LogicalPlan
,这里的 Unresolved 的意思说,还不知道 src
是否存在,或它的元数据是什么样,只有通过 Analysis 阶段后,才会把Unresolved 变成 Resolved LogicalPlan
。这里的意思可以理解为,读取名为 src
的表,但这张表的情况未知,有待验证。Analyzer 机制:生成 Analyzed LogicalPlan
一条语句 SELECT col FROM sales,当我们不知道 col 的具体类型(Int,String,还是其他),甚至是否在 sales 表中有col这一个列的时候,就称之为是 Unresolved 的。
而在 analysis 阶段,就是将 Unresolved 的变成 Resolved 的。Spark SQL 通过使用 Catalyst rule 和 Catalog 来跟踪数据源的 table 信息。并对 Unresolved 应用如下的 rules(rule 可以理解为一条一条的规则,当匹配到树某些节点的时候就会被应用)。
analysis 阶段的流程:
- 从
Catalog
中,查询Unresolved LogicalPlan
中对应的关系(relations)
- 根据输入属性名(比如上述的 col 列),映射到具体的属性
- 确定哪些属性引用相同的值并赋予它们唯一的
exprId
- Propagating and coercing types through expressions,是对数据进行强制转换,方便后续对1 + col 这样的数据进行处理。
Optimizer 机制:生成 Optimized LogicalPlan
Optimizer
以及之后的模块都只会在触发了 action 操作后才会执行。优化器是用来将 Resolved LogicalPlan
转化为 Optimized LogicalPlan
的。Optimizer
就是根据大佬们多年的 SQL 优化经验来对语法树进行优化,比如谓词下推、列值裁剪、常量累加等。优化的模式和 Analyzer 非常相近,Optimizer 同样继承了RuleExecutor
,并定义了很多优化的 Rule。第六章 Spark SQL 物理计划(PhysicalPlan)
在 SparkSQL 中,物理计划用 SparkPlan 表示,从
Optimized LogicalPlan
传入到 SparkSQL 物理计划提交并执行,主要经过 3 个阶段,这 3 个阶段分别产生 Iterator[PhysicalPlan
、 SparkPlan
和 Prepared SparkPlan
,其中 Prepared SparkPlan
可以直接提交并执行。这 3 个阶段所做的工作分别如下:
- 由
SparkPlanner
将各种物理计划策略(Strategy)作用于对应的LogicalPlan
节点上,生成SparkPlan
列表(注:LogicalPlan
可能产生多种SparkPlan
)
- 选取最佳的
SparkPlan
,在 Spark2 版本中的实现较为简单,在候选列表中直接用 next() 方法获取第一个
- 提交前进行准备工作,进行一些分区排序方面的处理,确保
SparkPlan
各节点能够正确 执行,这一步通过prepareForExecution()
方法调用若干规则Rule
进行转换