圖書《資料資產管理核心技術與應用》核心章節節選-3.1.2. 從Spark 執行計劃中獲取資料血緣

张永清發表於2024-08-02

本文節選自清華大學出版社出版的圖書《資料資產管理核心技術與應用》,作者為張永清等著。

從Spark 執行計劃中獲取資料血緣

因為資料處理任務會涉及到資料的轉換和處理,所以從資料任務中解析血緣也是獲取資料血緣的渠道之一,Spark 是大資料中資料處理最常用的一個技術元件,既可以做實時任務的處理,也可以做離線任務的處理。Spark在執行每一條SQL語句的時候,都會生成一個執行計劃,這一點和很多資料庫的做法很類似,都是SQL語句在執行時,先生成執行計劃。如下圖3-1-10所示,在Spark的官方文件連結https://spark.apache.org/docs/latest/sql-ref-syntax-qry-explain.html#content中,有明確提到,可以根據EXPLAIN關鍵字來獲取執行計劃,這和很多資料庫檢視執行計劃的方式很類似。

圖3-1-10

Spark底層生成執行計劃以及處理執行計劃的過程如下圖3-1-11所示。本文節選自清華大學出版社出版的圖書《資料資產管理核心技術與應用》,作者為張永清等著。

圖3-1-11

從圖中可以看到,

1、 執行SQL語句或者Data Frame時,會先生成一個Unresolved Logical Plan,就是沒有做過任何處理和分析的邏輯執行計劃,僅僅會從SQL語法的角度做一些基礎性的校驗。

2、 之後透過獲取Catalog的資料,對需要執行的SQL語句做表名、列名的進一步分析校驗,從而生成一個可以直接執行的邏輯執行計劃。

3、 但是Spark底層會有個最佳化器來生成一個最優的執行操作方式,從而生成一個最佳化後的最佳邏輯執行計劃。

4、 將最終確定下來的邏輯執行計劃轉換為物理執行計劃,轉換為最終的程式碼進行執行。

Spark的執行計劃其實就是資料處理的過程計劃,會將SQL語句或者DataFrame 做解析,並且結合Catalog一起,生成最終資料轉換和處理的程式碼。所以可以從Spark的執行計劃中,獲取到資料的轉換邏輯,從而解析到資料的血緣。但是spark的執行計劃都是在spark底層內部自動處理的,如何獲取到每次Spark任務的執行計劃的資訊呢?其實在Spark底層有一套Listener的架構設計,可以透過Spark Listener 來獲取到spark 底層很多執行的資料資訊。

在spark的原始碼中,以Scala的形式提供了一個org.apache.spark.sql.util.QueryExecutionListener trait (類似Java 語言的介面),來作為Spark SQL等任務執行的監聽器。在org.apache.spark.sql.util.QueryExecutionListener 中提供瞭如下表3-1-2所示的兩個方法。

表3-1-2

方法名

描述

def onSuccess(funcName: String, qe: QueryExecution, durationNs: Long): Unit

執行成功時,呼叫的方法,其中包括了執行計劃引數,這裡的執行計劃可以是邏輯計劃或者物理計劃

def onFailure(funcName: String, qe: QueryExecution, exception: Exception): Unit

執行失敗時,呼叫的方法,其中同樣也包括了執行計劃引數,這裡的執行計劃可以是邏輯計劃或者物理計劃

因此可以借用QueryExecutionListener 來主動讓Spark在執行任務時,將執行計劃資訊推送到自己的系統或者資料庫中,然後再做進一步的解析,如下圖3-1-12所示。本文節選自清華大學出版社出版的圖書《資料資產管理核心技術與應用》,作者為張永清等著。

圖3-1-12

import org.apache.spark.internal.Logging
import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.execution.QueryExecution
import org.apache.spark.sql.util.QueryExecutionListener
case class PlanExecutionListener(sparkSession: SparkSession) extends QueryExecutionListener with Logging{

  override def onSuccess(funcName: String, qe: QueryExecution, durationNs: Long): Unit = withErrorHandling(qe) {
    // 執行成功時,呼叫解析執行計劃的方法
    planParser(qe)
  }

  override def onFailure(funcName: String, qe: QueryExecution, exception: Exception): Unit = withErrorHandling(qe) {

  }

  private def withErrorHandling(qe: QueryExecution)(body: => Unit): Unit = {
    try
      body
    catch {
      case NonFatal(e) =>
        val ctx = qe.sparkSession.sparkContext
        logError(s"Unexpected error occurred during lineage processing for application: ${ctx.appName} #${ctx.applicationId}", e)
    }
  }


  def planParser(qe: QueryExecution): Unit = {
    logInfo("----------- start to get spark  analyzed LogicPlan--------")
      //解析執行計劃,並且將執行計劃的資料傳送到自有的系統或者資料庫中
      ......
  }
} 

上面的程式碼中,實現了QueryExecutionListener 這個trait中的onSuccess和onFailure這兩個方法,只有在onSuccess時,才需要獲取執行計劃的資料,因為只有onSuccess時的血緣才是有效的。

實現好了自定義的QueryExecutionListener後,可以透過sparkSession.listenerManager.register來將自己實現的PlanExecutionListener 註冊到Spark會話中,listenerManager是Spark中Listener的管理器。

在獲取到執行計劃時,需要再結合Catalog一起,來進一步解析血緣的資料,如下圖3-1-13所示

圖3-1-13

Spark 中常見的執行計劃實現類如下表3-1-3所示,獲取資料血緣時,就是需要從如下的這些執行計劃中解析血緣關係。本文節選自清華大學出版社出版的圖書《資料資產管理核心技術與應用》,作者為張永清等著。

表3-1-3

執行計劃實現類

描述

org.apache.spark.sql.execution.datasources.LogicalRelation

一般用於解析欄位級的關聯關係

org.apache.spark.sql.catalyst.catalog.HiveTableRelation

Hive 表關聯關係的執行計劃,一般用於SQL執行時,存在關聯查詢的情況會出現該執行計劃。

org.apache.spark.sql.hive.execution.InsertIntoHiveTable

一般是在執行insert into 的SQL 語句時才會產生的執行計劃,例如insert into xxx_table(colum1,column2) values("4","zhangsan")

org.apache.spark.sql.execution.datasources

.InsertIntoHadoopFsRelationCommand

一般用於執行類似 sparkSession

.read

.table("xx_source_table ")

.limit(10)

.write

.mode(SaveMode.Append)

.insertInto("xx_target_table ")產生的執行計劃。

org.apache.spark.sql.hive.execution.

CreateHiveTableAsSelectCommand

一般是在執行create table xxx_table as的SQL 語句時才會產生的執行計劃,例如create table xx_target_table as select * from xx_source_table

org.apache.spark.sql.execution.command

.CreateDataSourceTableAsSelectCommand

一般用於執行類似sparkSession

.read

.table("xx_source_table")

.limit(10)

.write

.mode(SaveMode.Append)

.saveAsTable("xx_target_table")產生的執行計劃。

org.apache.spark.sql.execution.datasources

.InsertIntoDataSourceCommand

一般用於將SQL查詢結果寫入到一張表中,比如insert into xxx_target_table select * from xxx_source_table

如下是以org.apache.spark.sql.execution.datasources

.InsertIntoHadoopFsRelationCommand 為例的spark 執行計劃的資料,如下資料已經將原始的執行計劃轉換為了json格式的資料,方便做展示。

.................更多內容,請參考清華大學出版社出版的圖書《資料資產管理核心技術與應用》,作者為張永清等著

相關文章