Spark系列 - (3) Spark SQL

Spark

3. Spark SQL

3.1 Hive、Shark和Sparksql

Hive:Hadoop剛開始出來的時候,使用的是hadoop自帶的分佈式計算系統 MapReduce,但是MapReduce的使用難度較大,所以就開發了Hive。Hive的出現解決了MapReduce的使用難度較大的問題,Hive的運行原理是將HQL語句經過語法解析、邏輯計劃、物理計劃轉化成MapReduce程序執行。

Shark:2011年Shark誕生,即Hive on Spark。爲了實現與Hive兼容,Shark在HiveQL方面重用了Hive中HiveQL的解析、邏輯執行計劃、執行計劃優化等邏輯;可以近似認爲僅將物理執行計劃從MapReduce作業替換成了Spark作業,通過Hive 的HiveQL解析,把HiveQL翻譯成Spark上的RDD操作;Shark的出現,使得SQL-on-Hadoop的性能比Hive有了10-100倍的提高。

Shark的缺陷

  1. 執行計劃優化完全依賴於Hive,不方便添加新的優化策略

  2. 因爲Spark是線程級並行,而MapReduce是進程級並行,因此,Spark在兼容 Hive的實現上存在線程安全問題,導致Shark不得不使用另外一套獨立維護的打了補丁的Hive源碼分支。

2014年7月,spark團隊將Shark轉給Hive進行管理,Hive on Spark是一個Hive的也就是說,Hive將不再受限於一個引擎,可以採用Map-Reduce、Tez、Spark等引擎;

Spark SQL作爲Spark生態的一員誕生,不再受限於Hive,只是兼容Hive。

3.2 RDD和DataFrame、DataSet

RDD:彈性(Resilient)、分佈式(Distributed)、數據集(Datasets),具有隻讀、Lazy、類型安全等特點,具有比較好用的API。RDD的劣勢體現在性能限制上,它是一個JVM駐內存對象,這也就決定了存在GC的限制和數據增加時Java序列化成本的升高。

DataFrame:與RDD類似,DataFRame也是一個不可變的彈性分佈式數據集。除了數據以外,還記錄着數據的結構信息,即Schema。另外DataFrame API提供的是一套高層的關係操作,比函數式的RDD API要更加友好。DataFrame的查詢計劃可以通過Spark catalyst optimiser進行優化,即使 Spark經驗並不豐富,用dataframe寫得程序也可以儘量被轉化爲高效的形式予以執行。

DataFrame只是知道字段,但是不知道字段的類型,所以在執行這些操作的時候是 沒辦法在編譯的時候檢查是否類型失敗的。

上圖直觀地體現了 DataFrame 和 RDD 的區別。左側的 RDD[Person]雖然以Person爲類型參 數,但 Spark 框架本身不瞭解Person 類的內部結構。而右側的DataFrame卻提供了詳細的結構信息,使得Spark SQL 可以清楚地知道該數據集中包含哪些列,每列的名稱和類型各是什麼。 DataFrame是爲數據提供了Schema的視圖。可以把它當做數據庫中的一張表來對待,DataFrame也是懶執行的。性能上比 RDD 要高,主要原因:優化的執行計劃:查詢計劃通過 Spark catalyst optimiser 進行優化。

DataSet:DataSet是DataFrame的擴展,是Spark最新的數據抽象。Dataframe 是 Dataset 的特列,DataFrame=Dataset[Row] ,所以可以通過 as 方法將 Dataframe 轉換爲 Dataset。Row 是一個類型,跟Car、Person 這些的類型一樣,所有的表結構信息我都用 Row 來表示。DataSet 是強類型的。比如可以有 Dataset[Car],Dataset[Person]。DataFrame只是知道字段,但是不知道字段的類型,所以在執行這些操作的時候是沒辦法在編譯的時候檢查是否類型失敗的,比如你可以對一個String進行減法操作,在執行的時候才報錯,而DataSet不僅僅知道字段,而且知道字段類型,所以有更嚴格的錯誤檢查。就跟JSON對象和類對象之間的類比。

3.2.1 三者的共性

  • 都是分佈式彈性數據集,爲處理超大型數據提供便利;
  • 都是Lasy的,在進行創建、轉換,如map方法時,不會立即執行,只有在遇到Action如foreach時,三者纔會開始遍歷運算,極端情況下,如果代碼裏面有創建、 轉換,但是後面沒有在Action中使用對應的結果,在執行時會被直接跳過;
  • 都有partition的概念;
  • 三者有許多共同的函數,如filter,排序等;
  • DataFrame和Dataset均可使用模式匹配獲取各個字段的值和類型;
  • 三者可以相互轉化

3.2.2 區別

RDD與DataFrame/DataSet的區別

RDD:

  • 用於Spark1.X各模塊的API(SparkContext、MLLib,Dstream等)
  • 不支持sparksql操作
  • 不支持代碼自動優化

DataFrame與DataSet:

  • 用於Spark2.X各模塊的API(SparkSession、ML、StructuredStreaming等等)
  • 支持SparkSql操作,比如select,groupby之類,還能註冊臨時表/視窗,進行 sql語句操作
  • 支持一些方便的保存方式,比如保存成csv、json等格式
  • 基於sparksql引擎構建,支持代碼自動優化

DataFrame與DataSet的區別

DataFrame:

  • DataFrame每一行的類型固定爲Row,只有通過解析才能獲取各個字段的值, 每一列的值沒法直接訪問。
  • DataFrame編譯器缺少類型安全檢查。
testDF.foreach{ 
   line => val col1=line.getAs[String]("col1") 
   println(col1) 
   val col2=line.getAs[String]("col2") 
   println(col2) 
}

DataSet:

  • DataFrame和DataSet之間,可以看成JSON對象和類對象之間的類比。
  • DataSet是類型安全的。

3.2.3 Sql、dataframe、DataSet的類型安全

  • 如果使用Spark SQL的查詢語句,要直到運行時你纔會發現有語法錯誤(這樣做代價很大)。
  • 如果使用DataFrame,你在也就是說,當你在 DataFrame 中調用了 API 之外的函數時,編譯器就可以發現這個錯。但如果此時,使用了一個不存在字段的名字,則只能到運行時才能發現錯誤;
  • 如果用的是DataSet[Person],所有不匹配的類型參數都可以在編譯時發現;

3.2.4 什麼時候使用DataFrame或DataSet

下面的情況可以考慮使用DataFrame或Dataset,

  • 如果你需要豐富的語義、高級抽象和特定領域專用的 API,那就使用 DataFrame 或 Dataset;
  • 如果你的處理需要對半結構化數據進行高級處理,如 filter、map、aggregation、 average、sum、SQL 查詢、列式訪問或使用 lambda 函數,那就使用 DataFrame 或 Dataset;
  • 如果你想在編譯時就有高度的類型安全,想要有類型的 JVM 對象,用上 Catalyst 優化,並得益於 Tungsten 生成的高效代碼,那就使用 Dataset;
  • 如果你想在不同的 Spark 庫之間使用一致和簡化的 API,那就使用 DataFrame 或 Dataset;
  • 如果你是R或者Python使用者,就用DataFrame;

除此之外,在需要更細緻的控制時就退回去使用RDD;

3.2.5 RDD、DataFrame、DataSet之間的轉換

1. RDD轉DataFrame、Dataset

  • RDD轉DataFrame:一般用元組把一行的數據寫在一起,然後在toDF中指定字段名。

  • RDD轉Dataset:需要提前定義字段名和類型。

2. DataFrame轉RDD、Dataset

  • DataFrame轉RDD:直接轉 val rdd = testDF.rdd
  • DataFrame轉Dataset:需要提前定義case class,然後使用as方法。

3. Dataset轉RDD、DataFrame

  • DataSet轉RDD:直接轉 val rdd = testDS.rdd
  • DataSet轉DataFrame:直接轉即可,spark會把case class封裝成Row。

3.3 Spark SQL優化

Catalyst是spark sql的核心,是一套針對spark sql 語句執行過程中的查詢優化框架。因此要理解spark sql的執行流程,理解Catalyst的工作流程是理解spark sql的關鍵。而說到Catalyst,就必須提到下面這張圖了,這張圖描述了spark sql執行的全流程。其中,中間四步爲catalyst的工作流程。

參考:https://www.jianshu.com/p/0aa4b1caac2e

SQL語句首先通過Parser模塊被解析爲語法樹,此棵樹稱爲Unresolved Logical Plan;Unresolved Logical Plan通過Analyzer模塊藉助於Catalog中的表信息解析爲Logical Plan;此時,Optimizer再通過各種基於規則的優化策略進行深入優化,得到Optimized Logical Plan;優化後的邏輯執行計劃依然是邏輯的,並不能被Spark系統理解,此時需要將此邏輯執行計劃轉換爲Physical Plan。

Spark常見的優化策略有下面幾類:

  1. Combine Limits:合併Limit,就是將兩個相鄰的limit合爲一個。
  2. Constant Folding:常量疊加
  • NullPropagation:空格處理
  • BooleanSimplification:布爾表達式簡化
  • ConstantFolding:常量疊加
  • SimplifyFilters:Filter簡化
  • LikeSimplification:like表達式簡化。
  • SimplifyCasts:Cast簡化
  • SimplifyCaseConversionExpressions:CASE大小寫轉化表達式簡化
  1. Filter Pushdown Filter下推
  • CombineFilters Filter合併
  • PushPredicateThroughProject:通過Project下推
  • PushPredicateThroughJoin:通過Join下推
  • ColumnPruning:列剪枝

搜索『後端精進之路』並關注,立刻獲取文章合集和麪試攻略,還有價值數千元的面試大禮包等你拿。

後端精進之路.png

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章