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