SparkSQL 对 SQL 查询的优化静态优化和动态优化两大部分介绍

SparkSQL 对 SQL 查询的优化主要分为 静态优化动态优化 两大部分,其中静态优化主要在查询编译时进行,而动态优化则是在查询执行过程中进行。SparkSQL 的优化包括了多种技术,例如 RBO(基于规则的优化)CBO(基于成本的优化) ,以及 AQE(Adaptive Query Execution,适应性查询执行)。这些优化方法和技术可以显著提高查询的性能。

1. 静态优化(Static Optimization)

静态优化是在 SparkSQL 查询的解析和逻辑计划生成后,在物理计划生成前应用的优化。这些优化是在查询计划的编译阶段完成的,并不依赖于运行时数据特征。

主要的静态优化技术:
  • 基于规则的优化(RBO):规则驱动的优化方法,通过定义优化规则来改变查询的执行计划。
  • 基于成本的优化(CBO):通过对不同物理计划的成本进行评估,选择代价最小的执行计划。
RBO(基于规则的优化)

RBO 是 SparkSQL 默认的优化策略,基于一些预定义的规则对逻辑计划进行优化。规则通常包括:

  • 合并相邻的 Filter 操作
  • 常量折叠:将常量表达式提前计算。
  • 子查询合并:优化子查询,使其转化为连接操作。
  • 表达式简化:对不必要的运算进行简化。

这些规则定义在 SparkSQL 的 Optimizer 类中,通常是通过继承 Rule[LogicalPlan] 类来定义自定义规则。

示例 :合并连续的 Filter 操作。

scala 复制代码
object MergeFilters extends Rule[LogicalPlan] {
  def apply(plan: LogicalPlan): LogicalPlan = plan match {
    case Filter(condition1, Filter(condition2, child)) =>
      // 合并条件,避免多次扫描数据
      Filter(condition1 && condition2, child)
    case _ => plan
  }
}

这段代码演示了如何合并两个连续的 Filter 节点。假设我们有一个查询:

sql 复制代码
SELECT * FROM employees WHERE age > 30 AND age < 50;

在没有优化规则时,Catalyst 会生成两个 Filter 节点:

复制代码
Filter(age > 30)
  Filter(age < 50)
    Scan(employees)

通过 RBO 规则优化后,可以合并为:

复制代码
Filter(age > 30 AND age < 50)
  Scan(employees)

这样,执行时就只需要扫描一次数据。

CBO(基于成本的优化)

CBO 通过计算不同物理计划的代价来选择最优的执行计划。SparkSQL 会生成多个物理执行计划,然后使用一个启发式的代价模型来选择执行计划。代价模型通常包括以下几个因素:

  • 执行时间。
  • I/O 成本。
  • 内存使用等。

CBO 主要是在物理计划生成阶段进行的,因此它比 RBO 更加依赖于数据的实际特征,如数据的大小、分布等。

示例:选择 HashJoin 或 SortMergeJoin。

在某些情况下,SparkSQL 可能会选择使用 HashJoinSortMergeJoin,具体取决于数据的大小和分布。例如,如果两个表非常大,可能会选择 SortMergeJoin,因为它更适合处理大数据集,而对于较小的数据集,HashJoin 会更高效。

CBO 会基于数据统计信息(如数据的大小、列的分布等)来做出决策。

2. 动态优化(Dynamic Optimization)

动态优化是 Spark 在查询执行过程中根据实际的数据分布情况,调整执行计划的能力。最主要的动态优化技术是 AQE(Adaptive Query Execution,适应性查询执行)

AQE(适应性查询执行)

AQE 是 Spark 3.0 引入的一个新特性,它可以根据运行时的统计信息(例如,分区的大小和数据分布)动态调整查询的执行计划。AQE 会在查询执行过程中根据数据的实际分布,调整物理计划,解决静态优化时未能考虑到的性能瓶颈。

AQE 的核心优化技术:
  • 动态调整 Shuffle 分区数:在执行查询时,AQE 会根据每个阶段的数据量,动态调整 Shuffle 的分区数。这样可以避免过多的 Shuffle 分区导致性能下降。
  • 动态选择 Join 策略:在执行过程中,AQE 会动态选择最优的 Join 策略(例如,决定使用 HashJoin 或 SortMergeJoin)。
  • 处理 Skew Join:在某些情况下,数据分布不均可能导致某个分区的数据量过大(数据倾斜)。AQE 可以动态调整处理策略,避免某个节点超载。

AQE 工作流程:

  1. 生成初始物理计划:Spark 在静态优化后生成一个初步的物理执行计划。
  2. 执行并收集统计信息:在执行过程中,Spark 会动态地收集执行过程中每个阶段的统计信息(例如,分区的大小)。
  3. 重新优化:根据收集到的统计信息,Spark 会重新调整物理执行计划。

示例:动态调整 Shuffle 分区数。

scala 复制代码
spark.conf.set("spark.sql.adaptive.enabled", "true")
spark.conf.set("spark.sql.adaptive.shuffle.targetPostShuffleInputSize", "134217728")

在启用 AQE 后,Spark 会根据每个阶段的数据量动态调整 Shuffle 分区数,确保每个分区的数据量接近设定的目标(例如上面的 128MB)。

3. 总结:

  • 静态优化:包括 RBO 和 CBO,主要在查询编译阶段应用。RBO 使用预定义的规则进行优化,而 CBO 通过评估不同计划的成本来选择最佳计划。
  • 动态优化(AQE) :在查询执行过程中,根据运行时的统计信息动态调整执行计划,解决静态优化时无法预测的性能瓶颈问题。
    • RBO:通过一系列的优化规则对逻辑计划进行优化。
    • CBO:通过评估不同物理计划的代价,选择最优的执行计划。
    • AQE:在运行时动态调整执行计划,处理 Shuffle 分区调整、Join 策略选择、Skew Join 等问题。

通过这些优化方法,SparkSQL 能够在保证正确性的基础上显著提高查询的性能。

相关推荐
小技工丨1 个月前
SparkSQL全之RDD、DF、DS ,UDF、架构、资源划分、sql执行计划、调优......
大数据·spark·sparksql·spark调优
howard20052 个月前
SparkSQL数据模型综合实践
数据模型·dataframe·sparksql
songqq275 个月前
Spark中给读取到的数据 的列 重命名的几种方式!
spark·sparksql
一个散步者的梦9 个月前
HIVE及SparkSQL优化经验
数据仓库·hive·hadoop·sparksql
莫待花无空折枝9 个月前
经典sql
大数据·hive·sql·sparksql
chde2Wang1 年前
SparkSQL学习03-数据读取与存储
学习·sparksql·数据读取与存储
有语忆语1 年前
SparkSQL基础解析(三)
dataset·dataframe·sparksql