Hive數據傾斜解決思路

Hive數據傾斜總結

發生傾斜的根本原因在於,shuffle之後,key的分佈不均勻,使得大量的key集中在某個reduce節點,導致此節點過於“忙碌”,在其他節點都處理完之後,任務的結整需要等待此節點處理完,使得整個任務被此節點堵塞,要解決此問題,主要可以分爲兩大塊:一是儘量不shuffle;二是shuffle之後,在reduce節點上的key分佈儘量均勻。

傾斜探查

join key傾斜

select key, count(*) cnt 
from B b 
group by key
order by cnt desc;

如果某個key的cnt比較大,比如排在第1的key,cnt佔了一大半,那使用這個key去join,極有可能發生數據傾斜。
探查結果
像圖中,排在第1的key,佔據了4161w條記錄,佔表總記錄數的一半,如果使用這個key去join,運行時肯定傾斜。

查看日誌

如果發現,某個任務運行時間比較長,可以看下執行日誌,如果運行時reduce階段一直在99%,則極可能是發生了數據傾斜,如下圖:
在這裏插入圖片描述

傾斜原因

  • key相同的太集中,導致傾斜

  • key的哈希結果,或分區函數的結果,導致key分佈集中

解決方案

只要有shuffle,就有可能產生數據傾斜,解決數據傾斜的總體思路是:

  • 能在map端處理的,不留到reduce端(當然,這不僅是解決數據傾斜的思路,這個適用所有的優化):
    • 根據謂詞下推規則,儘早過濾數據;
    • 使用map Join
  • 將key值儘量分散。
    • 使用隨機數打散,但要考慮不能影響結果。
    • 提高並行度

具體的方法有以下:MapJoin,手工分割,添加隨機前綴,使用列桶表等。

1. mapjoin

  • 適應場景:Join的兩個表中,有一個表比較小,小到可以完全放到分佈式緩存中,默認的大小是25M。

  • 配置方法(親測有效):

    set hive.auto.convert.join = true;
    set hive.mapjoin.smalltable.filesize=25000000;
    
  • 優點:運行時轉換成mapjoin,無reduce階段,運行時間極短

  • 缺點:適用場景有限,需要佔用分佈式緩存

mapJoin原理

  • Local work:
    • read records via standard table scan (including filters and projections) from source on local machine
    • build hashtable in memory
    • write hashtable to local disk
    • upload hashtable to dfs
    • add hashtable to distributed cache
  • Map task
    • read hashtable from local disk (distributed cache) into memory
    • match records’ keys against hashtable
    • combine matches and write to output
  • No reduce task

2. 手工分割

  • 適應場景:當我們知道某些少量極個別的key 去join時會發生傾斜,我們可以手工將其分開執行,比如key=A時會發生傾斜,我們此時可以將所有的key分成兩塊來執行,一塊是key=A,另一塊是key<>A,如下:

  • 實踐代碼(親測有效)

    select t1.* 
    from t1 join t2 on t1.key=t2.key
    
    拆成以下SQL:
    
    select t1.* 
    from t1 join t2 on t1.key=t2.key
    where t1.key=A
    union all 
    select t1.*
    from t1 join t2 on t1.key=t2.key
    where t1.key<>A
    
  • 缺點:場景有限

    這種場景需要注意一下:如果傾斜的key,除了key=A外,還有其他key時也會發生傾斜時,並不一定適合去拆分。

3. 大表添加N種隨機前綴,小表膨脹N倍數據

  • 適用場景:小表不是很小,不太方便使用mapjoin。
  • 解決方法:對大表的key添加N種隨機前綴,對小表和N種隨機前綴做笛卡爾積,使得大表裏新生成的key在小表裏都有可能正確地找到相應的key,解決思路可參考以下圖:
    在這裏插入圖片描述
  • 實踐代碼(親測有效),以下代碼是N的取值爲10
select a.*
from a
left join (
    select concat(c.rand_num,'_',d.key) as key from(
        select rand_num from dual LATERAL VIEW explode(array(0,1,2,3,4,5,6,7,8,9)) rand_num_list as rand_num
    )c join d
)b on concat(cast(9*rand() as int), '_', a.key) =b.key
where a.ds='2019-09-05' 
  • 優點:可適當降低傾斜程度。
  • 缺點:N的取值不太好判斷,需要多次實驗,而且數據膨脹後,會增加資源消耗。

4. 使用Skewed Table或List Bucketing Table

  • Skewed Table vs. List Bucketing Table

    • Skewed Table是傾斜表,元數據存儲了傾斜key的信息,例如:
    create table T (c1 string, c2 string) skewed by (c1) on ('x1') 
    
    • List Bucketing Table是一種特殊的傾斜表,數據存儲的時候會帶有傾斜key的子目錄,創建時需要指定STORED AS DIRECTORIES,例如:
    create table T (c1 string, c2 string) skewed by (c1) on ('x1') stored as directories;
    create table T (c1 string, c2 string, c3 string) skewed by (c1, c2) on (('x1', 'x2'), ('y1', 'y2')) stored as directories;
    
    • skewed table 不會創建子目錄,但list bucketing table會。
  • 適用場景:

    • 每個分區中,傾斜key的數據佔了絕大部分,比如表中的key有10個,但key=key1的數據佔了總數據量的60%;
    • 每個分區中,傾斜的key比較少,因爲太多容易給元數據造成壓力。
  • 不適用場景:

    • 如果傾斜的key的數量比較多,會給元數據存儲造成壓力
  • Skewed table 使用方法 :

set hive.optimize.skewjoin.compiletime =true;
set hive.optimize.skewjoin =true;
  • List Bucketing Table不指定配置參數,直接Join,就能生效

5. 配置每個reduce節點處理的數據量

  • 對於hive作業配置如下:
set hive.exec.reducers.bytes.per.reducer = 100000000;
  • 配置:在Hive0.14.0版本之前,此配置默認爲1G(1,000,000,000),在版本0.14.0之後,默認爲256MB

  • 優點:reduce個數增多,可以提高速度

  • 缺點:但這個閾值不太好控制,還是容易產生傾斜。

  • 對於spark作業
    對於spark作業,可以採用spark自適應特性來緩解數據傾斜問題,配置如下:

屬性名稱 默認值 備註
spark.sql.adaptive.enabled false 自適應執行框架的開關
spark.sql.adaptive.shuffle.targetPostShuffleInputSize 67108864 動態調整reduce個數的 partition 大小依據,如設置64MB,則reduce 階段每個task最少處理 64MB的數據。
spark.sql.adaptive.shuffle.targetPostShuffleRowCount 20000000 動態調整reduce個數的partition條數依據,如設置20000000則reduce階段每個task最少處理 20000000條的數據。

可以通過配置調整reduce階段每個task處理的數據量的大小或處理的記錄條數,經緩解數據傾斜問題

其他

以下兩種方式都試過,但沒感覺到提速。

set hive.optimize.skewjoin=true;
set hive.skewjoin.key=100000; --如果在同一個join operator出現超過此閾值的相同的key,則認爲此key爲傾斜key
set mapred.reduce.tasks=800;
  • 優點:reduce個數增多,可以提高速度
  • 缺點:但這個閾值不太好控制,還是容易產生傾斜。

參考文獻:

http://www.jasongj.com/spark/skew/

https://cwiki.apache.org/confluence/display/Hive/Skewed+Join+Optimization

https://cwiki.apache.org/confluence/display/Hive/ListBucketing

https://cwiki.apache.org/confluence/display/Hive/LanguageManual+JoinOptimization#LanguageManualJoinOptimization-PriorSupportforMAPJOIN

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