天天看點

MaxCompute計算長尾問題

長尾問題是分布式計算裡最常見的問題之一,也是典型的疑難雜症。究其原因,是因為資料分布不均,導緻各個節點的工作量不同,整個任務就需要等最慢的節點完成才能完成。處理這類問題的思路就是把工作分給多個Worker去執行,而不是一個Worker單獨抗下最重的那份工作。本文希望就平時工作中遇到的一些典型的長尾問題的場景及其解法做一些分享。

但是如果兩張表都比較大,就需要先盡量去重。實在不行的話,就需要從業務上考慮,為什麼會有這樣的兩個大資料量的Key要做笛卡爾積,能否從業務上進行優化。

Group By Key 出現長尾的原因是因為某個Key内的計算量特别大。

對SQL進行改寫,添加随機數,把長Key進行拆分是解決Group By長尾的一種比較好的方法,具體的原理也比較容易懂。對于一個SQL:

不考慮Combiner,M節點會Shuffle到R上,然後R再做Count操作。對應的執行計劃是M->R

但是如果對長尾的Key再做一次工作再配置設定,就變成:

可以看到,這次的執行計劃變成了M->R->R。雖然執行的步驟變長了,但是長尾的Key經過了2個步驟的處理,整體的時間消耗可能反而有所減少。不過大家也可以很容易看出來,如果資料的長尾并不嚴重,用這種方法人為地增加一次R的過程,最終的時間消耗可能反而更大。

對于這種優化方法,有一個通用的實作方法,就是使用系統參數,設定

但是通用性的優化政策無法針對具體的業務進行分析,得出的結果不總是最優的。開發人員也可以根據實際的資料情況,用更加高效的方法來改寫SQL。

可以看到,對于Distinct,上面的政策已經不生效了。對這種場景,我們可以考慮:

可以改寫成

可以看到,我們把Distinct改成了普通的Count,這樣的計算壓力不會落到同一個Reducer上。而且這樣改寫後,既能支援前面提到的Group By優化,系統又能做Combiner,性能會有較大的提升。

動态分區功能為了整理小檔案,會在最後起一個Reduce,對資料進行整理。是以如果使用動态分區寫入資料時有傾斜的話,就會發生長尾。

另外就平時經驗來看,濫用動态分區的功能也是産生這類長尾的一個常見原因。如果寫入的資料已經确定需要把資料寫入某個具體分區,那可以在Insert的時候指定需要寫入的分區,而不是使用動态分區。

對于MapRedcuce作業,使用Combiner是一種常見的長尾優化政策。在WordCount的例子裡,就已經有提到這種做法。通過Combiner,減少Maper Shuffle往Reducer的資料,可以大大減少網絡傳輸的開銷。對于MaxCompute SQL,這種優化會由系統自動完成。

需要注意的是,Combiner隻是Map端的優化,需要保證是否執行Combiner的結果是一樣的。以WordCount為例,傳2個(KEY,1)和傳1個(KEY,2)的結果是一樣的。但是比如在做平均值的時候,就不能在Combiner裡就把(KEY,1)和(KEY,2)合并成(KEY,1.5)。

其實針對長尾這種場景,除了前面提到的Local Combiner,MaxCompute系統本身還做了一些優化。比如在跑任務的時候,日志裡突然打出這樣的内容(+N backups部分):

可以看到1047個Reducer,有1046個已經完成了,但是最後一個一直沒完成。系統識别出這種情況後,自動啟動了一個新的Reducer,跑一樣的資料,然後看兩個哪個快,取快的資料歸并到最後的結果集裡。

雖然前面的優化政策有很多,但是實際上還是有限。有時候碰到長尾問題,還需要從業務角度上想想是否有更好的解決方法,比如:

實際資料可能包含非常多的噪音,比如如果想根據通路者的ID進行計算,看看每個使用者的通路記錄的行為,那就需要先去掉爬蟲的資料(不過現在的爬蟲也越來越難識别了)。否則爬蟲資料很容易長尾計算的長尾。類似的情況還包含根據xxid進行關聯的時候,需要考慮這個關聯字段是否存在為空的情況。

因為業務的情況,總會有一些特殊情況,比如ISV的操作記錄,在資料量、行為方式上都會和普通的個人會有很大的差別。那麼可以考慮針對大客戶,使用特殊的分析方式進行單獨處理。

資料分布不均勻的情況下,不要使用常量字段做Distribute by字段來實作全排序。

我首先想看看資料的分布,跑了個SQL

結果因為前面提到的造資料的時候長尾資料多複制了一些,長尾嚴重地把backup的優化政策都給跑出來了

MaxCompute計算長尾問題
MaxCompute計算長尾問題

我們這時候不能直接拿到這個資料就直接想技術上怎麼優化,作為資料分析師,需要先了解資料,看下這個key是什麼。雖然Count()多的Count(Distinct item_id)不一定多,但是一般來說Count()和其他人明顯不一樣的資料總是有問題的,可以從這裡入手。于是跑了個

很快跑好了,結果如下(這個長尾造的有點多)。不過如果怕Count(*)和Count(Distinct item_id)的Key不同,可以參考本文最後的SQL

MaxCompute計算長尾問題

正想看看是不是就是這個Key(15330397)的問題,前面的那個長尾也跑好了(跑了25:13),一看,還真是

MaxCompute計算長尾問題

這麼明顯的資料肯定是異常資料了,回頭到底是爬蟲還是什麼特殊情況再單獨研究,這裡根據之前的政策,有2種方法:

直接對異常資料做單獨處理,這裡不計算異常資料:

這個SQL很快,一分鐘就得出結果。根據這個結果做個可視化,就能夠很清楚看到天貓移動端使用者的購買情況了。但是有個缺點就是我們這裡的長尾比較少,就造了一條。但是實際上,可能會有很多爬蟲在爬資料,那就沒辦法在SQL裡寫死這些過濾的USER_ID。我們可以考慮先取到這些長尾的資料後做mapjoin過濾異常資料,具體的實作這裡就不做了。

另外我們再試試前面提到的MRR的方法以進行SQL改寫:

這個SQL一共花了(3:25)。

通過這兩種方法的實驗,我們更好地了解到資料裡的資訊,同時更快地得出結果。

如果有問題,可以加入我們的釘釘群來咨詢

MaxCompute計算長尾問題