spark2 sql原理分析—執行計劃的生成和處理(QueryExecution實現概要)

概述

本文分析Dataset中的執行計劃的處理過程。執行計劃的處理包括以下幾個過程:分析邏輯執行計劃->優化邏輯執行計劃->生成一個或多個物理執行計劃->優化物理執行計劃->生成可執行代碼。
這個過程都是在dataset的成員:QueryExecution中完成。所以本篇,就是介紹QueryExecution的實現過程。
由於QueryExecution的實現過程很多,本文主要介紹QueryExecution的概要流程。

QueryExecution要點

QueryExecution是:使用Spark執行關係查詢的主要工作流程。 通過它可以允許開發人員能夠很容易的訪問查詢執行的中間階段。
在QueryExecution中實現了:把一個邏輯計劃生成物理執行計劃的全過程。

QueryExecution的實現要點概要如下:

  1. QueryExecution獲取一個sparkSession.sessionState.planner,這是一個優化器,其實現類是SparkPlanner, 該planner會把一個優化後的邏輯計劃轉換成一個或多個物理執行計劃。
 protected def planner = sparkSession.sessionState.planner
  1. 通過sparkSession.sessionState.analyzer.executeAndCheck來檢查一個邏輯執行計劃,並得到一個分析和檢查後的邏輯計劃:analyzed。
  // 1.對邏輯計劃進行分析,得到分析後的邏輯計劃
  lazy val analyzed: LogicalPlan = {
    SparkSession.setActiveSession(sparkSession)
    sparkSession.sessionState.analyzer.executeAndCheck(logical)
  }
  1. 通過sparkSession.sharedState.cacheManager.useCachedData把analyzed進行緩存,或更新cacheManager中的檢查後的邏輯執行計劃。
  // 對分析後的邏輯計劃進行緩存,更新緩存中的計劃
  lazy val withCachedData: LogicalPlan = {
    assertAnalyzed()
    assertSupported()
    sparkSession.sharedState.cacheManager.useCachedData(analyzed)
  }
  1. 通過sparkSession.sessionState.optimizer對邏輯執行計劃進行優化,得到優化後的邏輯執行計劃:optimizedPlan。
  // 2.對分析後的邏輯計劃進行優化,得到優化後的邏輯計劃
  lazy val optimizedPlan: LogicalPlan = sparkSession.sessionState.optimizer.execute(withCachedData)
  1. 通過sparkSession.sessionState.planner的plan函數把optimizedPlan轉換成物理執行計劃,其實得到一個SparkPlan的變量:sparkPlan。
  // 3.把優化後的邏輯計劃轉換成一個或多個物理執行計劃
  lazy val sparkPlan: SparkPlan = {
    SparkSession.setActiveSession(sparkSession)
    // TODO: We use next(), i.e. take the first plan returned by the planner, here for now,
    //       but we will implement to choose the best plan.
    planner.plan(ReturnAnswer(optimizedPlan)).next()
  }
  1. 調用prepareForExecution函數對物理執行計劃進行優化,得到新的物理執行計劃:executedPlan。
  // 4.對物理執行計劃進行優化,得到優化後的物理執行計劃
  lazy val executedPlan: SparkPlan = prepareForExecution(sparkPlan)
  1. 定義了一個toRdd函數,該函數把物理執行計劃轉換成rdd的執行代碼:toRdd: RDD[InternalRow] = executedPlan.execute()。
  // 5.把物理執行計劃轉換成rdd代碼
  lazy val toRdd: RDD[InternalRow] = executedPlan.execute()

實戰

查看dataframe的queryExecution信息。就可以查看該dataframe的從邏輯計劃到物理計劃的生成過程。
如下:

scala> val df2 = df.withColumn("new_col", df("_c3"))
df2: org.apache.spark.sql.DataFrame = [_c0: string, _c1: string ... 4 more fields]

scala> val df = spark.read.csv("/usr/testdata/events")
df: org.apache.spark.sql.DataFrame = [_c0: string, _c1: string ... 3 more fields]

scala> df2.queryExecution.toString
res20: String =
== Parsed Logical Plan ==
Project [_c0#13, _c1#14, _c2#15, _c3#16, _c4#17, _c3#16 AS new_col#82]
+- Relation[_c0#13,_c1#14,_c2#15,_c3#16,_c4#17] csv

== Analyzed Logical Plan ==
_c0: string, _c1: string, _c2: string, _c3: string, _c4: string, new_col: string
Project [_c0#13, _c1#14, _c2#15, _c3#16, _c4#17, _c3#16 AS new_col#82]
+- Relation[_c0#13,_c1#14,_c2#15,_c3#16,_c4#17] csv

== Optimized Logical Plan ==
Project [_c0#13, _c1#14, _c2#15, _c3#16, _c4#17, _c3#16 AS new_col#82]
+- Relation[_c0#13,_c1#14,_c2#15,_c3#16,_c4#17] csv

== Physical Plan ==
*(1) Project [_c0#13, _c1#14, _c2#15, _c3#16, _c4#17, _c3#16 AS new_col#82]
+- *(1) FileScan csv [_c0#13,_c1#14,_c2#15,_c3#16,_c4#17] Batched: false, Format: CSV, Location: InMemoryFileIndex[hdfs://localhost:9000/usr/testdata...

參考

  • https://www.slideshare.net/databricks/deep-dive-into-spark-sql-with-advanced-performance-tuning-with-xiao-li-wenchen-fan
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章