Spark Analyzed LogicalPlan 的生成过程用的规则

Spark SQL 解析之后是 Unresolved Logical Plan,经过 Analyzer 之后变为 Resolved Logical Plan。
用的规则组成一个 batches: Seq[Batch]。一个 Batch 包含一组规则和这组规则的执行次数限制。

1. Batch Substitution(替换操作的 Batch)

1.1 CTESubstitution

with 表达式替换。

1.2 WindowsSubstitution

匹配到 WithWindowDefinition 表达式时,将其子节点中未解析的窗口函数表达式(UnsolvedWindowExpression)转换成窗口函数表达式。

1.3 EleminateUnions(消除 Union 操作)

当 union 算子节点只有一个子节点时,Union 操作实际上并没有起到作用,这种情况下应该消除该 Union 节点。
如以下 SQL 生成的执行计划去掉了 union。

spark-sql> explain select * from t1 
  union all 
           select * from t1 where 1=2;
plan
== Physical Plan ==
Scan hive test.t1 [c1#19], HiveTableRelation [`test`.`t1`, org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe, Data Cols: [c1#19], Partition Cols: []]


Time taken: 0.459 seconds, Fetched 1 row(s)

1.4 SubstituteUnresoledOrdinals

Order byGroup by 语句可以用常熟表示下标。分别用参数spark.sql.orderByOrdinalspark.sql.groupByOrdinal 控制,默认为 true

如以下示例中,HashAggregate 的 keys 变为了 c1,而不是 1。

spark-sql> explain select c1 from t1 group by 1;
plan
== Physical Plan ==
HashAggregate(keys=[c1#51], functions=[])
+- Exchange hashpartitioning(c1#51, 1), ENSURE_REQUIREMENTS, [plan_id=104]
   +- HashAggregate(keys=[c1#51], functions=[])
      +- Scan hive test.t1 [c1#51], HiveTableRelation [`test`.`t1`, org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe, Data Cols: [c1#51], Partition Cols: []]


Time taken: 0.161 seconds, Fetched 1 row(s)

2. Batch Resolution

包含 ResolveNamespace(catalogManager) , ResolveRelations, ResolveTables 等25条规则。

3. Nondeterminstic

仅包含一个规则 PullOutNondeterministic。将非 Project 或非 Filter 算子中的 nondeterministic 表达式提取出来,然后将这些表达式放在内层的 Project 算子中或最终的 Project 算子中。

4. HandleNullInputsForUDF

对用户自定义函数做特除处理。如果自定义函数的参数有基本数据类型,生成 if 表达式。
示例

  • 表的定义如下:
spark-sql> desc test.tab1;
col_name	data_type	comment
a1                  	int                 	                    
a2                  	int                 	                    
Time taken: 0.322 seconds, Fetched 2 row(s)

spark-shell --master local[2] 进入 spark-shell

val doublePara =  (i:Int) => 2 * i
val doubleUDF = udf(doublePara)
spark.udf.register("doubleUDF", doubleUDF)
spark.sql("explain select doubleUDF(a1) from test.tab1").collect().foreach(println)

输出结果如下,可以看到,由于 a1 列的值可能为 null,则生成表达式为if (isnull(a1#20)) null else doubleUDF(knownnotnull(a1#20)) AS doubleUDF(a1)#22

[== Physical Plan ==
Project [if (isnull(a1#20)) null else doubleUDF(knownnotnull(a1#20)) AS doubleUDF(a1)#22]
+- Scan hive test.tab1 [a1#20], HiveTableRelation [`test`.`tab1`, org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe, Data Cols: [a1#20, a2#21], Partition Cols: []]

]

5. UpdateAttributeNullability(FixNullability)

在 DataFrame 或 DataSet 等编程接口中,用户代码对于某些列的操作可能改变其 nullability 属性。本规则更新属性的 nullability,如以下示例,b.id 是非空的,但是a join b 之后,b.id 字段变为可能为 null。
参考 SPARK-13484, SPARK-13801

val a = spark.range(10).select(col("id"), lit(0).as("count"))
val b = spark.range(10).select((col("id") % 3).as("id")).groupBy("id").count()
a.join(b, a("id") === b("id"), "left_outer").filter(b("count").isNull).show()

6. Cleanup

清理无用的别名信息。仅仅在最上级的 project 列表或者汇聚或者 windows 函数的别名不能去掉,其他的都可以去掉。
以下的查询中,子查询把 c1 别名为 c2,外部把 c2 别名为 c3。优化后,去掉 c2,外部把 c1 别名为 c3。

spark-sql> explain select c2 c3 from (select c1 c2 from t1)t;
plan
== Physical Plan ==
Project [c1#42 AS c3#36]
+- Scan hive test.t1 [c1#42], HiveTableRelation [`test`.`t1`, org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe, Data Cols: [c1#42], Partition Cols: []]


Time taken: 0.316 seconds, Fetched 1 row(s)

你可能感兴趣的:(spark,spark,大数据,分布式)