一、概述

所有的策略都继承自 GenericStrategy 类,其中定义了 planLater 和 apply 方法;SparkStrategy 继承自 GenericStrategy 类,对其中的 planLater 进行了实现,根据传入的 LogicalPlan 直接生成前述提到的 PlanLater 节点 。 此外,在 Spark SQL 中

Strategy 是 SparkStrategy 类的别名

1
type Strategy = SparkStrategy

在 Spark SQL中,当逻辑计划处理完毕后,会构造 SparkPlanner 并执行 $plan()$ 方法对 LogicalPlan 进行处理,得到对应的物理计划。一个逻辑计划可能会对应多个物理计划,因此,SparkPlanner 得到的是一个物理计划的列表(Iterator[SparkPlan])。
SparkPlanner 继承自 SparkStrategies 类,而 SparkStrategies 类则继承自 QueryPlanner 基类,重要的 $plan()$ 方法实现就在 QueryPlanner 类中。

SparkStrategies 类本身不提供任何方法,而是在内部提供一批 SparkPlanner 会用到的各种策略(Strategy)实现。最后,在 SparkPlanner 层面将这些策略整合在一起,通过 $plan$ 方法进行逐个应用。

类似逻辑计划阶段的 Anaylzer 和 Optimizer, SparkPlanner 本身只是一个逻辑的驱动,各种策略的 $apply()$ 方法把逻辑执行计划算子映射成物理执行计划算子。

在 SparkPlanner 的调用逻辑和各种策略中,PlanLater 随处可见。根据其实现,PlanLater 本身也是 SparkPlan 的一种,区别在于 $doExecute$ 方法没有实现,表示不支持执行,所起到的作用仅仅是占位,等待后续步骤处理。

二、实现

物理计划执行策略都继承自 GenericStrategy 类,其中定义了 planLater 和 apply 方法。

1
2
3
4
5
abstract class GenericStrategy[PhysicalPlan <: TreeNode[PhysicalPlan]] extends Logging {

protected def planLater(plan: LogicalPlan): PhysicalPlan
def apply(plan: LogicalPlan): Seq[PhysicalPlan]
}

SparkStrategy 继承自 GenericStrategy 类,对其中的 planLater 进行了实现,根据传入的 LogicalPlan 直接生成PlanLater 节点。各种具体的 Strategy 都实现了 $apply$ 方法,将传入的 LogicalPlan 转换为 SparkPlan 的列表。

如果当前的执行策略无法应用于该 LogicalPlan 节点,则返回的物理执行计划列表为空。
因此,Strategy 是生成物理算子树的基础。

在实现上,各种 Strategy 会匹配传入的 LogicalPlan 节点,根据节点或节点组合的不同情形实行一对一的映射或多对一的映射。一对一的映射方式比较直观,以 BasicOperators 为例,该Strategy 实现了各种基本操作的转换,其中列出了大量的映射关系,包括 Sort 对应 SortExec、Union 对应 UnionExec 等。

多对一的情况涉及对多个 LogicalPlan 节点进行组合转换,这里称为逻辑算子树的模式匹配。