當咱們寫一個查詢語句時,通常包含三個部分,select部分,from數據源部分,where限制條件部分,這三部分的內容在sql中有專門的名稱:mysql
當咱們寫sql時,如上圖所示,在進行邏輯解析時會把sql分紅三個部分,project,DataSource,Filter模塊,當生成執行部分時又把他們稱爲:Result模塊、算法
DataSource模塊和Opertion模塊。sql
那麼在關係數據庫中,當咱們寫完一個查詢語句進行執行時,發生的過程以下圖所示:數據庫
整個執行流程是:query -> Parse -> Bind -> Optimize -> Execute編程
一、寫完sql查詢語句,sql的查詢引擎首先把咱們的查詢語句進行解析,也就是Parse過程,解析的過程是把咱們寫的查詢語句進行分割,把project,DataSource和Filter三個部分解析出來從而造成一個邏輯解析tree,在解析的過程當中還會檢查咱們的sql語法是否有錯誤,好比缺乏指標字段、數據庫中不包含這張數據表等。當發現有錯誤時當即中止解析,並報錯。當順利完成解析時,會進入到Bind過程。json
二、Bind過程,經過單詞咱們可看出,這個過程是一個綁定的過程。爲何須要綁定過程?這個問題須要咱們從軟件實現的角度去思考,若是讓咱們來實現這個sql查詢引擎,咱們應該怎麼作?他們採用的策略是首先把sql查詢語句分割,分割不一樣的部分,再進行解析從而造成邏輯解析tree,而後須要知道咱們須要取數據的數據表在哪裏,須要哪些字段,執行什麼邏輯,這些都保存在數據庫的數據字典中,所以bind過程,其實就是把Parse過程後造成的邏輯解析tree,與數據庫的數據字典綁定的過程。綁定後會造成一個執行tree,從而讓程序知道表在哪裏,須要什麼字段等等緩存
三、完成了Bind過程後,數據庫查詢引擎會提供幾個查詢執行計劃,而且給出了查詢執行計劃的一些統計信息,既然提供了幾個執行計劃,那麼有比較就有優劣,數據庫會根據這些執行計劃的統計信息選擇一個最優的執行計劃,所以這個過程是Optimize(優化)過程。數據結構
四、選擇了一個最優的執行計劃,那麼就剩下最後一步執行Execute,最後執行的過程和咱們解析的過程是不同的,當咱們知道執行的順序,對咱們之後寫sql以及優化都是有很大的幫助的.執行查詢後,他是先執行where部分,而後找到數據源之數據表,最後生成select的部分,咱們的最終結果。執行的順序是:operation->DataSource->Result架構
雖然以上部分對sparkSQL沒有什麼聯繫,可是知道這些,對咱們理解sparkSQL仍是頗有幫助的。app
要想對這個框架有一個清晰的認識,首先咱們要弄清楚,咱們爲何須要sparkSQL呢?我的建議通常狀況下在寫sql可以直接解決的問題就不要使用sparkSQL,若是想刻意使用sparkSQL,也不必定可以加快開發的進程。使用sparkSQL是爲了解決通常用sql不能解決的複雜邏輯,使用編程語言的優點來解決問題。咱們使用sparkSQL通常的流程以下圖:
如上圖所示,通常狀況下分爲兩個部分:a、把數據讀入到sparkSQL中,sparkSQL進行數據處理或者算法實現,而後再把處理後的數據輸出到相應的輸出源中。
一、一樣咱們也是從若是讓咱們開發,咱們應該怎麼作,須要考慮什麼問題來思考這個問題。
a、第一個問題是,數據源有幾個,咱們可能從哪些數據源讀取數據?如今sparkSQL支持不少的數據源,好比:hive數據倉庫、json文件,.txt,以及orc文件,同時如今還支持jdbc從關係數據庫中取數據。功能很強大。
b、還一個須要思考的問題是數據類型怎麼映射啊?咱們知道當咱們從一個數據庫表中讀入數據時,咱們定義的表結構的字段的類型和編程語言好比scala中的數據類型映射關係是怎 樣的一種映射關係?在sparkSQL中有一種來解決這個問題的方法,來實現數據表中的字段類型到編程語言數據類型的映射關係。這個之後詳細介紹,先了解有這個問題就行。
c、數據有了,那麼在sparkSQL中咱們應該怎麼組織這些數據,須要什麼樣的數據結構呢,同時咱們對這些數據均可以進行什麼樣的操做?sparkSQL採用的是DataFrame數據結構來組織讀入到sparkSQL中的數據,DataFrame數據結構其實和數據庫的表結構差很少,數據是按照行來進行存儲,同是還有一個schema,就至關於數據庫的表結構,記錄着每一行數據屬於哪一個字段。
d、當數據處理完之後,咱們須要把數據放入到什麼地方,並切以什麼樣的格式進行對應,這個a和b要解決的問題是相同的。
二、sparkSQL對於以上問題,的實現邏輯也很明確,從上圖,已經很清楚,主要分爲兩個階段,每一個階段都對應一個具體的類來實現。
a、 對於第一個階段,sparkSQL中存在兩個類來解決這些問題:HiveContext,SQLContext,同事hiveContext繼承了SQLContext的全部方法,同事又對其進行了擴展。由於咱們知道, hive和mysql的查詢仍是有必定的差異的。HiveContext只是用來處理從hive數據倉庫中讀入數據的操做,SQLContext能夠處理sparkSQL可以支持的剩下的全部的數據源。這兩個類處理的粒度是限制在對數據的讀寫上,同事對錶級別的操做上,好比,讀入數據、緩存表、釋放緩存表表、註冊表、刪除註冊的表、返回表的結構等的操做。
b、sparkSQL處理讀入的數據,採用的是DataFrame中提供的方法。由於當咱們把數據讀入到sparkSQL中,這個數據就是DataFrame類型的。同時數據都是按照Row進行存儲的。其中 DataFrame中提供了不少有用的方法。之後會細說。
c、在spark1.6版本之後,又增長了一個相似於DataFrame的數據結構DataSet,增長此數據結構的目的DataFrame有軟肋,他只能處理按照Row進行存儲的數據,而且只能使用DataFrame中提供的方法,咱們只能使用一部分RDD提供的操做。實現DataSet的目的就是讓咱們可以像操做RDD同樣來操做sparkSQL中的數據。
d、其中還有一些其餘的類,可是如今在sparkSQL中最主要的就是上面的三個類,其餘類之後碰到了會慢慢想清楚。
hiveContext和SQLContext與我第一部分講到的sql語句的模塊解析實現的原理實際上是同樣的,採用了一樣的邏輯過程,而且網上有好多講這一塊的,就直接粘貼複製啦!!
sqlContext總的一個過程以下圖所示:
1.SQL語句通過SqlParse解析成UnresolvedLogicalPlan;
2.使用analyzer結合數據數據字典(catalog)進行綁定,生成resolvedLogicalPlan;
3.使用optimizer對resolvedLogicalPlan進行優化,生成optimizedLogicalPlan;
4.使用SparkPlan將LogicalPlan轉換成PhysicalPlan;
5.使用prepareForExecution()將PhysicalPlan轉換成可執行物理計劃;
6.使用execute()執行可執行物理計劃;
7.生成SchemaRDD。
在整個運行過程當中涉及到多個SparkSQL的組件,如SqlParse、analyzer、optimizer、SparkPlan等等
hiveContext總的一個過程以下圖所示:
1.SQL語句通過HiveQl.parseSql解析成Unresolved LogicalPlan,在這個解析過程當中對hiveql語句使用getAst()獲取AST樹,而後再進行解析;
2.使用analyzer結合數據hive源數據Metastore(新的catalog)進行綁定,生成resolved LogicalPlan;
3.使用optimizer對resolved LogicalPlan進行優化,生成optimized LogicalPlan,優化前使用了ExtractPythonUdfs(catalog.PreInsertionCasts(catalog.CreateTables(analyzed)))進行預處理;
4.使用hivePlanner將LogicalPlan轉換成PhysicalPlan;
5.使用prepareForExecution()將PhysicalPlan轉換成可執行物理計劃;
6.使用execute()執行可執行物理計劃;
7.執行後,使用map(_.copy)將結果導入SchemaRDD。