本文節選自清華大學出版社出版的圖書《資料資產管理核心技術與應用》,作者為張永清等著。
從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格式的資料,方便做展示。
.................更多內容,請參考清華大學出版社出版的圖書《資料資產管理核心技術與應用》,作者為張永清等著