1. 程式人生 > >hive join 資料傾斜 真實案例

hive join 資料傾斜 真實案例

hive或者MR處理資料,不怕資料量大,就怕傾斜。hive裡大表join的時候,資料傾斜就是個很頭疼的問題。本博主就遇到了一個真實案例,特意記錄下來,有需要的同學可以參考

1.查了5個小時還沒結束的sql語句

set mapred.reduce.tasks = 30;
insert overwrite directory 'xxx'
select
cus.idA,cus.name,addr.bb from tableA as cus
join tableB as addr
on cus.idA = addr.idB

很簡單的一個hql語句,優化的空間也不是很大(例子中的addr資料量比cus小,應該講addr放在前面驅動join)。tableA的量級為億級,tableB的量級為幾百萬級別。就這麼一個簡單的sql,尼瑪從上午十點半開始跑,跑到下午三點半還沒有跑完。實在受不了了,kill掉了。

2.初步分析

首先上個查詢過程中的圖
這裡寫圖片描述

看到這種情況,稍微有點經驗的同學第一反應肯定就是:臥槽,這尼瑪肯定是資料傾斜了。沒錯,map早就完工了,reduce階段一直卡在99%,而且cumulative cpu的時間還一直在增長,說明整個job還在後臺跑著。這種情況下,99%的可能性就是資料發生了傾斜,整個查詢任務都在等某個節點完成。。。

3.分析那部分資料產生了傾斜

問題既然已經定位了,那接下來就是需要解決問題了。正好不巧的是,叢集這幾天還出了一些狀況。so,首先為了確認到底是叢集本身的問題,還是程式碼的問題,先找了另外兩個表,都是億級資料。這兩個表不存在資料傾斜的情況,join一把試了試,兩分鐘之內結果就出來了。萬幸,說明這會叢集已經沒有問題了,還是查查資料跟程式碼吧。

程式碼本身很簡單,那就沿著資料傾斜的方向查查吧。因為上面的兩個表是根據id關聯的,那如果傾斜的話,肯定就是id傾斜了哇。

set mapred.reduce.tasks = 5;
select idA,count(*) as num
from tableA
group by idA
distribute by idA
sort by num desc limit 10

結果為:

192928  5828529
2000000000496592833 2406289
18000   1706031
4000288 1386324
2000000003624295444 1201178
2000000001720892923 1029475
2000000002292880478 991299
2000000000736661289 881954
2000000000740899183 873487
2000000000575115116 803250

對於有上億資料的一個表來說,這資料也算不上傾斜多厲害嘛。最多的一個key也就五百多萬不到六百萬。好吧,先不管了,再查一把另外一個表

set mapred.reduce.tasks = 5;
select idB,count(*) as num
from tableB
group by idB
distribute by idB
sort by num desc limit 10

結果也很快出來

192928  383412
18000   60318
617279581   23028
51010262    4643
4000286 3528
2000000000575115116 3218
1366173280  3012
4212339 2972
2000000002025620390 2704
2000000001312577574 2622

這資料傾斜,也不是特別嚴重嘛。

不過再把這兩個結果一對比,尼瑪恍然大悟。兩個表裡最多的一個key都是192928,一個出現了將近600萬次,一個出現了將近40萬次。這兩個表再一join,尼瑪這一個key就是600萬*40萬的計算量。最要命的是,這計算量都分配給了一個節點。我數學不太好,600萬*40萬是多少,跪求數學好的同學幫忙計算一下。不過根據經驗來看的話,別說5個小時,再添個0也未必能算得完。。。

4.如何解決

既然找到了資料傾斜的位置,那解決起來也就好辦了。因為本博主的真正需求並不是真正要算兩個表的笛卡爾積(估計實際中也極少有真正的需求算600萬*40萬資料的笛卡爾積。如果有,那畫面太美我不敢看),所以最easy的解決方案,就是將這些key給過濾掉完事:

set mapred.reduce.tasks = 30;
insert overwrite directory 'xxx'
select
cus.idA,cus.name,addr.bb from tableA as cus
join tableB as addr
on cus.idA = addr.idB
where cus.idA not in (192928,2000000000496592833,18000,4000288,2000000003624295444,2000000001720892923,2000000002292880478,2000000000736661289,2000000000740899183,2000000000575115116,617279581,51010262,4000286,1366173280,2000000002025620390,2000000001312577574)

將此程式碼重新提交,5min時間,job跑完收工!