Spark SQL 架构简介
简单看一下Spark SQL 的架构。下面这张图描述了一条 SQL 提交之后需要经历的几个阶段,结合这些阶段就可以看到在哪些环节可以做优化。
很多时候,做数据仓库建模的同学更倾向于直接写 SQL 而非使用 Spark 的 DSL。一条 SQL 提交之后会被 Parser 解析并转化为 Unresolved Logical Plan。它的重点是 Logical Plan 也即逻辑计划,它描述了希望做什么样的查询。Unresolved 是指该查询相关的一些信息未知,比如不知道查询的目标表的 Schema 以及数据位置。
上述信息存于 Catalog 内。在生产环境中,一般由 Hive Metastore 提供 Catalog 服务。Analyzer 会结合 Catalog 将 Unresolved Logical Plan 转换为 Resolved Logical Plan。
到这里还不够。不同的人写出来的SQL 不一样,生成的 Resolved Logical Plan 也就不一样,执行效率也不一样。为了保证无论用户如何写 SQL 都可以高效的执行,Spark SQL 需要对 Resolved Logical Plan 进行优化,这个优化由 Optimizer 完成。Optimizer 包含了一系列规则,对 Resolved Logical Plan 进行等价转换,最终生成 Optimized Logical Plan。该 Optimized Logical Plan 不能保证是全局最优的,但至少是接近最优的。
上述过程只与 SQL 有关,与查询有关,但是与 Spark 无关,因此无法直接提交给 Spark 执行。Query Planner 负责将 Optimized Logical Plan 转换为 Physical Plan,进而可以直接由 Spark 执行。
由于同一种逻辑算子可以有多种物理实现。如 Join 有多种实现,ShuffledHashJoin、BroadcastHashJoin、BroadcastNestedLoopJoin、SortMergeJoin 等。因此 Optimized Logical Plan 可被 Query Planner 转换为多个 Physical Plan。如何选择最优的 Physical Plan 成为一件非常影响最终执行性能的事情。一种比较好的方式是,构建一个 Cost Model,并对所有候选的 Physical Plan 应用该 Model 并挑选 Cost 最小的 Physical Plan 作为最终的 Selected Physical Plan。
Physical Plan 可直接转换成 RDD 由 Spark 执行。我们经常说“计划赶不上变化”,在执行过程中,可能发现原计划不是最优的,后续执行计划如果能根据运行时的统计信息进行调整可能提升整体执行效率。这部分动态调整由 Adaptive Execution 完成。
后面介绍字节跳动在 Spark SQL 上做的一些优化,主要围绕这一节介绍的逻辑计划优化与物理计划优化展开。