摘要: 回顧大數據技術領域大事件,最先可追溯到06年Hadoop的正式啓動,而環顧四下,圍繞着數據庫及數據處理引擎,業內充斥着各類各樣的大數據技術。在雲棲社區2017在線技術峯會大數據技術峯會上,阿里雲大數據計算平臺架構師林偉作了題爲《MaxCompute的大腦:基於代價的優化器》的分享,爲你們分享阿里巴巴大數據計算服務的大腦——基於代價的優化器的設計和架構。算法
更多精彩內容參見雲棲社區大數據頻道https://yq.aliyun.com/big-data;此外,經過Maxcompute及其配套產品,低廉的大數據分析僅需幾步,詳情訪問https://www.aliyun.com/produc...。數據庫
摘要:回顧大數據技術領域大事件,最先可追溯到06年Hadoop的正式啓動,而環顧四下,圍繞着數據庫及數據處理引擎,業內充斥着各類各樣的大數據技術。這是個技術人的好時代,僅數據庫領域熱門DB就有300+,圍繞着Hadoop生態圈的大數據處理技術更是繁花似錦。在雲棲社區2017在線技術峯會大數據技術峯會上,阿里雲大數據計算平臺架構師林偉作了題爲《MaxCompute的大腦:基於代價的優化器》的分享,爲你們分享阿里巴巴大數據計算服務的大腦——基於代價的優化器的設計和架構。編程
MaxCompute簡介安全
大數據計算服務(MaxCompute)是一種快速、徹底託管的PB/EB級數據倉庫解決方案,MaxCompute具有萬臺服務器擴展能力和跨地域容災能力,是阿里巴巴內部核心大數據平臺,承擔了集團內部絕大多數的計算任務,支撐每日百萬級做業規模。MaxCompute向用戶提供了完善的數據導入方案以及多種經典的分佈式計算模型,可以更快速的解決用戶海量數據計算問題,有效下降企業成本,並保障數據安全。服務器
MaxCompute架構網絡
MaxCompute基本的體系結構如上圖所示,最底層就是在物理機器之上打造的提供統一存儲的盤古分佈式文件存儲系統;在盤古之上一層就是伏羲分佈式調度系統,這一層將包括CPU、內存、網絡以及磁盤等在內的全部計算資源管理起來;再上一層就是統一的執行引擎也就是MaxCompute執行引擎;而在執行引擎之上會打造各類各樣的運算模式,好比流計算、圖計算、離線處理、內存計算以及機器學習等等;在這之上還會有一層相關的編程語言,也就是MaxCompute語言;在語言上面但願爲各應用方可以提供一個很好的平臺,讓數據工程師可以經過平臺開發相關的應用,並使得應用可以快速地在分佈式場景裏面獲得部署運行。架構
MaxCompute的研發思路機器學習
MaxCompute的研發思路主要分爲如下四個方面:編程語言
高性能、低成本和大規模。但願打造的MaxCompute平臺可以提運算的高性能,儘量下降用戶的使用成本,而且在規模上面可以達到萬臺機器以及多集羣的規模。分佈式
穩定性,服務化。但願MaxCompute平臺可以提供穩定性和服務化的方式,使得用戶不用過多地考慮分佈式應用的難度,而只須要注重於用戶須要進行什麼樣的計算,讓系統自己服務於用戶,並可以提供穩定性,服務化的接口。
易用性,服務於數據開發者。但願MaxCompute平臺是易用的,而且可以很方便地服務於數據開發工程師,不須要數據工程師對於分佈式的場景進行很深的理解,而只要關注於須要用這些數據進行什麼樣的運算就能夠,接下來就是由MaxCompute平臺幫助數據開發工程師高效而且低成本地執行本身的想法。
多功能。但願MaxCompute可以具備更多的功能,不只僅是支持流計算、圖計算、批處理和機器學習等,而但願更多種類的計算可以在MaxCompute平臺上獲得更好的支持。
MaxCompute的大腦——優化器
基於以上的研發思路,MaxCompute平臺須要擁有一個更增強大的大腦,這個大腦須要更加理解用戶的數據,更加理解用戶的計算,而且更加理解用戶自己,MaxCompute的大腦須要可以幫助用戶更加高效地優化運算,經過系統層面去理解用戶到底須要進行什麼樣的運算,從而達到以前提到的各類目的,使得用戶可以從分佈式場景中脫離出來,沒必要去考慮如何才能使得運算高效地執行,而將這部分工做交給MaxCompute的大腦,讓它來爲用戶提供更智能的平臺,這也就是MaxCompute所可以爲用戶帶來的價值。
那麼MaxCompute的大腦到底是什麼呢?其實就是優化器。優化器可以將全部信息串聯在一塊兒,經過理解系統中數據的相關性以及用戶的企圖,並經過機器的能力去充分地分析各類各樣的環境,在分佈式場景中以最高效的方式實現對於用戶運算的執行。在本次分享中以離線計算做爲主要例子來對於MaxCompute的大腦——優化器進行介紹。
首先對於離線計算的概念進行簡單介紹,MaxCompute離線計算架構設計如上圖所示。在計算層面每每會存在一個相似高級語言的腳本語言,MaxCompute提供的是類SQL的腳本語言,將腳本語言經過FrontEnd提交進來,以後通過處理轉化成爲邏輯執行計劃,邏輯執行計劃在Optimizer(優化器)的指導下翻譯成更加高效的物理執行計劃,並經過與Runtime的鏈接以後由伏羲分佈式調度系統將物理執行計劃分解到運算節點上進行運算。
上述過程的核心就在於如何充分地理解用戶的核心計劃並經過優化獲得高效的物理執行計劃,這樣的過程就叫作優化器Optimizer。目前開源社區內的Hive以及Spark的一些優化器基本上都是基於規則的優化器,其實對於優化器而言,單機系統上就存在這樣的分類,分紅了基於規則的優化器和基於代價的優化器。
在單機場景裏面,Oracle 6-9i中使用的是基於規則的優化器,在Oracle 8開始有了基於代價的優化器,而Oracle 10g則徹底取代了以前基於規則的優化器;而在大數據場景裏面,像Hive最開始只有基於規則的優化器,而新版的Hive也開始引入了基於代價的優化器,可是Hive中還並非正真意義上的代價優化器。而MaxCompute則使用了徹底的基於代價的優化器。那麼這兩種優化器有什麼區別呢?其實基於規則的優化器理論上會根據邏輯模式的識別進行規則的轉換,也就是識別出一個模式就可能觸發一個規則將執行計劃從A改爲B,可是這種方式對數據不敏感,而且優化是局部貪婪的,就像登山的人只看眼前10米的範圍內哪裏是向上的,而不考慮應該先向下走才能走到更高的山頂,因此基於規則的優化器容易陷入局部優可是全局差的場景,容易受應用規則的順序而生產迥異的執行計劃,因此每每結果並非最優的。而基於代價的優化器是經過Volcano火山模型,嘗試各類可能等價的執行計劃,而後根據數據的統計信息,計算這些等價執行計劃的「代價」,最後從中選用代價Cost最低的執行計劃,這樣能夠達到全局的最優性。
這裏分享一個具體的例子幫助你們理解爲何基於規則的優化器沒法實現全局的最優化。上圖中的這段腳本的意思就是先在A、B和C上面作完join,join出來的結果在某一列上面進行group by操做並計算出平均值。能夠將上述的查詢過程畫成樹形的邏輯執行計劃,在數據庫領域每每是bottom-up的,也就是對於邏輯計劃樹而言,葉子節點是輸入,最終的目標輸出則是根節點,因此最終的數據流向是從下向上的。能夠看到在這個邏輯計劃裏面,首先是對於A、B、C三個表進行join,假設Size(B)<Size(C)<Size(A),也就是B、C這兩張表的大小是比A小的,這樣就能夠得到另外一種執行的方案就是先將B和C進行join以後再與A進行join,這以後再進行計算平均值,這樣的作法B和C join的中間結果從機率上面就會比較小,再與A join以後最終產生的結果規模也就會比較小,可是後面還須要對於B的c2列計算平均值,因此若是先作完B和C的join,而在第二次join時須要按照join的條件進行partition分區,須要按照A表的c1列和B表的c1列進行分區,可是後面須要在B表的c2列上計算平均值,這時候就會引入一個改變。由於在作完join以後,其實partition分區是在A表或者B表的c1上面的,可是要作的group by倒是在B表的c2上面的,因此須要引入exchange,這樣就會引入額外的data shuffling。而若是A、B、C三張表的大小差異並不大,其實就能夠先讓A和B進行join以後再與C進行join,這樣第二次join正好是在B的c2列上進行的,因此在接下來進行計算平均值的時候就不須要引入額外的data shuffling,雖然進行join的時候代價比原來高,可是由於省去了一次data shuffling,因此從全局上來看則是更爲優化的,這個例子就說明了基於規則的優化器能夠獲得基於局部優化,可是可能沒法獲得全局的優化。
基於代價的優化器則採用了不一樣的方案,它首先經過火山模型將查詢展開成爲多個等價的可執行計劃。例子中能夠先讓A和B join以後再join C或者先讓B和C join以後再join A,在這兩個計劃中,由於下面的計劃中多了一個exchange,而對於基於代價的優化器而言會在最後面有一個Cost代價模型,經過計算髮現第一個計劃在Cost上面更優,因此就會選擇最優的計劃進行執行。在基於代價的優化器中作了不少分佈式場景之下特有的Cost模型,而且考慮到了Non-SQL,由於不少場景是與互聯網有關的應用,用戶須要不少Non-SQL的支持,因此能夠經過用戶自定義函數幫助用戶實現一些Non-SQL與關係數據結合的查詢優化,最後還有一些多種分佈式場景的優化,這也是基於代價的優化器區別於單機優化器所作的一些工做。
接下來分享一下Volcano火山模型的相關,其實Volcano模型是代價模型的一個引擎,這個引擎其實在單機系統上面就已經提出來了。Volcano模型裏面也會有一些規則,可是與基於規則的優化器中的規則不一樣,這裏面的規則更像是一些轉化函數。Volcano模型首先會對於邏輯執行計劃進行分組,以後在組上面要完成一件工做,就會先在組裏面探索局部的表達式,而後根據一些規則應用一些變換,這些變換原則上都是代數等價的,在每次進行等價變化的時候其實並非取代原來的邏輯執行計劃樹,而是在本來的基礎之上分裂出新樹。因此最後將會出現不少個等價的執行計劃樹,最終能夠經過基於代價的優化器去選取最好的執行計劃。Volcano模型的原則是首先但願每一個規則更加局部性,也就是局部性和正交的規則越好,就越可以使得對於空間探索得更加全面。舉個例子,若是在平面上定義了先後左右四個方向,那麼就能夠經過這四個方向搜索整個二維平面的任何一個點,一樣的優化問題就是在空間裏選取最好的計劃,那麼就但願在每一次變化時候的探索規則都可以正交,這樣就能夠用更少的規則去探索整個空間,這樣如何去探索空間和選取探索最優路徑就能夠交給引擎了。
前面分享的比較抽象,這裏進一步進行舉例說明,但願可以加深你們對於優化過程的理解。假設有一個很是複雜的邏輯執行計劃樹,這就是真正須要作的用戶的任務,如今將其中一小部分提取出來,在進行計劃的優化時首先會分析有沒有已有的規則能夠與模式匹配,假設圖中的兩個節點正好能與模式匹配,一個是filter一個是project,理論上filter想要推到葉節點,也就是越早進行filter越好,如今就有一個模式:若是filter出如今project之上,也就是須要先作filter以後進行project,這樣就能夠轉換成另外一種計劃,將這兩個節點變成新的節點,也就是能夠將filter和project換順序,這樣就是應用規則的過程。一樣的還有另一個節點,好比是aggregate操做可以與其餘的模式匹配,以後就能夠尋找對應的規則,並轉化出等價的節點操做,這樣就能夠經過複用一棵樹節點的模式來維護多棵樹,在這裏例子中能夠看到使用了兩個規則,看上去節點上是隻是一個存儲,可是實際上卻描述了四棵等價樹。以後會對於這四棵等價樹花費的代價進行計算,最後選取花費代價最低的樹做爲執行計劃。總體的基於代價的優化過程就是這樣,可是能夠看到當邏輯計劃樹規模很大而且規則變化有不少種的狀況下,整個的探索空間仍是很是龐大的,因此須要在不少因素上對於優化過程進行考慮。
接下來爲你們介紹一下優化引擎的大體算法,下圖是簡化後的優化引擎算法,而在真正實施時還有不少須要考慮的因素下圖中並無表示出來。
首先會將一個邏輯執行計劃中的全部邏輯節點都註冊進去,註冊進去的同時就會將規則與已有的邏輯模式進行匹配,而後將匹配成功的規則推到規則隊列裏面,而後循環地彈出規則隊列中的規則,並真正地應用這個規則。固然應用規則存在兩種條件,一種就是應用以後可以產生等價樹,也就是可以在樹的局部分裂出另一種樹形狀態,而在分裂出來的樹上面也可能與其餘的模式匹配,若是局部範圍內的所有規則都已經匹配完成,就能夠開始計算花費的代價。而當經過計算代價得出最佳方案以後,就能夠放棄在該局部進行繼續優化,若是認爲當前的計劃仍然不是最優的,就能夠將該Cost記錄下來,繼續優化樹的其餘部分,直到最終找到最佳計劃。
分佈式查詢中的優化問題實例
在這裏給你們列舉一些在分佈式系統中有別與單機系統中分佈式查詢中的優化問題的實例。
例1其實很簡單,就是對於兩個表進行join操做,T1已經按照a,b進行了分區;T2已經按照a進行了分區,join的條件就是T1.a=T2.a。一種方法由於T1是按照a和b分區好的,join條件在a上面,因此須要對於T1按照a從新進行分區以後再與T2進行join。可是若是T1表很是大,遠遠大於T2表的規模,這時候就不想將T1按照從新進行分區,反而能夠採用另外一種方案,就是將T2做爲一個總體,將T2的全部數據廣播給T1每個數據,由於join條件是在a上面作內鏈接,因此能夠作這樣的選擇,這樣就能夠避免將很大的數據進行reshuffle。在這個場景中,如何去感知join的條件是關鍵。上圖例子中的兩個計劃並不存在絕對的最優,而是須要根據的數據的大小、T2數據量以及T1數據分片的分佈狀況來決定哪種方案纔是最優解,對於這個問題在SIFMOD12上面有不少的論文進行了討論,在此就再也不展開詳細的敘述。
再分享分佈式優化問題的裏另一個例子,如圖所示,T1和T2仍是在a上面進行join,join完成以後會有一個條件限制T1.a>20,完成以後會進行project,並將完成的結果當作新的一列b,最終但願全部的結果是order by b的。T1和T2都是range partition好了,這裏不是hash partition,並且由於已經進行了global sort,因此這裏在作join的時候就能夠利用到兩個表之間的range partition boundary,而不須要從新reshuffle數據,好比目前已經知道大於20會在哪些分區裏面出現,能夠根據選擇的boundary去讀取相應的數據以後進行做爲,能夠儘可能避免數據shuffling,在作完join以後,還會有一個用戶定義方法,將這個方法出來的結果按照order by b的規則進行排序,假設這個foo()方法是單調遞增的函數,這樣就能夠利用上面的條件也就是已經按照範圍分區好了,通過join和foo()還能保存b的順序,就不用引入一個exchange,能夠直接order by b操做。這樣就是分佈式中的一個查詢優化,也就是若是可以理解數據裏面的分片,可以知道數據的分佈式狀況還能理解用戶的自定義函數方法,以及這些方法經過什麼樣的途徑與優化器進行互動,就能夠對於分佈式查詢進行優化。這實際上是經過了用戶的Annotation就能夠知道用戶的方法具備什麼樣的特性,可以保持什麼樣的數據屬性。
用戶自定義函數UDF
在分佈式系統特別是Non-SQL中須要大量的用戶定義函數來進行擴展,由於不少查詢過程不是像join和aggregate這麼簡單的,而須要對於不少比較獨特的功能進行建模,因此須要用戶自定義的函數實現。一旦有了用戶自定義的函數,優化器每每難以理解UDF,那麼優化的範圍將會極大地受到限制,如上圖中的中間黃色的節點包含了用戶自定義的函數,可是可能系統並不知道這個函數所作的事情,那麼在優化的時候就可能分紅三個更小的可優化片斷,在在三個小片斷中進行進一步優化。若是優化器可以理解用戶自定義的函數在作什麼事情,那麼就可讓優化器穿透UDF達到更大範圍的優化。那麼UDF有什麼特性可以幫助優化器穿透它呢?其實能夠分析UDF是否是Row-wise操做的,考慮它是否是一行一行處理,不存在跨行的,考慮UDF是否是單調函數,是否是在處理時有些列是不變的,也就是能夠穿透的,它是否是能夠保持數據分片或者保持排序,以及在Cost上面的一些信息,它的Selectivity高仍是低,以及data distribution of output是多仍是少等等都能優化器更好地優化,爲優化器打開更大的優化空間,實現更加靈活的優化,幫助Cost模型選出更優的方案。這也是阿里巴巴目前在MaxCompute優化器上正在作的一些工做。
優化規則
MaxCompute基於代價的優化器作了大量的優化,實現以下圖所示的各類優化,這裏就不展敘述開了。能夠從下圖中看到在查詢中有不少優化能夠去作,這些全部的優化在整個系統引擎上面都是一個個算子,這些算子也在變化圖,產生了不少個等價的樹,由優化的引擎經過Cost模型去選擇最佳的方案。
Cost模型
什麼是Cost模型呢?其實Cost模型最須要關注的就是自己的代價模型。每一個Cost模型都須要關注於局部,好比input是什麼樣的Cost,通過join以後又會獲得什麼樣的Cost,而不須要關注於全局,全局方案的Cost則是由引擎經過累計獲得的總體Cost。好的Cost模型力求可以反映客觀的物理實現,Cost模型不須要獲得和真實如出一轍,Cost模型的最終目的是但願區別方案的優劣,只須要可以選出較優的計劃,並不須要Cost的絕對值具備什麼樣的特性。如今傳統的數據庫的Cost模型仍是很早之前的模型,就算硬件結構已經發生了變化,只要仍是馮諾依曼體系結構,架構沒有發生改變,Cost模型就能夠用於選擇最優的方案。
其實優化器還有不少其餘方面的因素能夠考慮,好比在規則方面,須要根據規則進行等價的變換,最後根據Cost模型選取最優的方案。隨着邏輯計劃規模的變大,若是枚舉全部可能的方案就會極大地耗費時間,特別是在MaxCompute上但願邏輯執行計劃越大越好,由於這樣就能給優化引擎更大的空間,可是這就帶來當枚舉全部的計劃時,有些枚舉的計劃實際上是沒必要要的,可能已經處於在一個不優化的狀況下了。因此如何去作有效的剪枝,如何去避免沒必要要的探索空間,也是實現一個好的優化器所須要考慮的。另外對探索空間的選擇,能夠將時間用在最有多是最優化的計劃的空間上面,這多是一個比較好的選擇,由於不能但願經過NP-hard的時間去選擇最優的計劃,而應該但願在有限的時間內選取比較好的執行計劃,因此在優化領域中其實不必定須要尋找最佳的方案,而是要避免最差的方案,由於在優化上面總會存在時間約束。
爲何基於代價的優化器對於MaxCompute平臺愈來愈重要了呢?
這是由於阿里巴巴但願能從Hive的一條條查詢語句中走出來,提供更加複雜的存儲過程。在上圖中有一個展現,能夠經過變量賦值以及預處理if-else編寫出更加複雜的查詢過程和存儲過程,而基於規則的優化器會由於貪婪算法而越走越偏,最終極可能得不到全局最優方案,而邏輯計劃的複雜化使得能夠優化的空間變大了,可是同時也使得對於優化器的要求變得更高,因此須要更好的基於代價的優化器幫助選擇比較好的執行計劃。而在分佈式以及Non-SQL等新型的場景下,使用基於代價的優化器有別於傳統單機優化器的方式,因此須要有對於數據、運算和用戶更加深入的理解來使得基於代價的優化器更加智能。
理解數據
那麼展開來看,什麼叫作理解數據呢。在數據格式方面,理解數據須要對於更多的數據索引以及異構的數據類型進行理解,對於結構化的數據、非結構化的數據以及半結構化的數據都進行理解,而在大數據的場景裏面數據是有一些Power-law屬性的,有百萬稀疏列的表格,須要在這樣的場景下實現一個更好的優化;理解數據也須要理解豐富的數據分片方式,這是在分佈式場景中才有的,數據分片能夠是Range/Hash/DirectHash的,而存儲能夠是Columnstorage/Columngrouping的,還須要用Hierarchy Partition來進行分級分區;還會須要理解完善的數據統計信息和運行時數據,須要理解Histogram、Distinct value以及Data Volume等等。
理解運算
從理解運算方面,須要更加理解用戶自定義的函數,可以與優化器進行互動,更夠讓用戶經過Annotation的方式顯示在運算中數據的屬性上具備的特性,使得能夠進行全局範圍的優化。在運行時也會進行更多的優化,好比會在中間運行到必定階段時須要判斷數據量的大小,根據數據量的大小進行並行化的選擇,並根據數據的位置選擇網絡拓撲上的優化策略。還能夠作實時性,規模性,性能,成本,可靠性之間的平衡,而且使用網絡Shuffling作內存計算以及流計算等。
理解用戶
從理解用戶的角度,須要理解在優化器上的用戶場景,理解多租戶場景下用戶對規模,性能,延時以及成本不一樣需求等,並在這樣的場景下讓優化器選取最佳的方案;在生態上面,優化器是核心的優化引擎,但願可以在語言上面更多地開放,但願能與更多的語言和生態進行對接,也但願可以提供強大的IDE能來爲開發者提供完整的開發體驗;最後但願可以在統一的平臺上提供多種運算的模式,使得優化器真正可以成爲運算的大腦。
閱讀更多幹貨好文,請關注掃描如下二維碼: