Hive 數(shù)據(jù)傾斜總結 - 菠蘿大數(shù)據(jù)夢工廠(Free World) - 博客頻道 - CSDN.NET http://blog.csdn.net/jiangshouzhuang/article/details/46591869
在做Shuffle階段的優(yōu)化過程中波岛,遇到了數(shù)據(jù)傾斜的問題银舱,造成了對一些情況下優(yōu)化效果不明顯。主要是因為在Job完成后的所得到的Counters是整個Job的總和叔磷,優(yōu)化是基于這些Counters得出的平均值栓袖,而由于數(shù)據(jù)傾斜的原因造成map處理數(shù)據(jù)量的差異過大哼蛆,使得這些平均值能代表的價值降低东帅。Hive的執(zhí)行是分階段的,map處理數(shù)據(jù)量的差異取決于上一個stage的reduce輸出巩踏,所以如何將數(shù)據(jù)均勻的分配到各個reduce中秃诵,就是解決數(shù)據(jù)傾斜的根本所在。規(guī)避錯誤來更好的運行比解決錯誤更高效塞琼。在查看了一些資料后菠净,總結如下。
1數(shù)據(jù)傾斜的原因
1.1操作:
關鍵詞
情形
后果
Join
其中一個表較小,
但是key集中
分發(fā)到某一個或幾個Reduce上的數(shù)據(jù)遠高于平均值
大表與大表毅往,但是分桶的判斷字段0值或空值過多
這些空值都由一個reduce處理牵咙,灰常慢
group by
group by 維度過小,
某值的數(shù)量過多
處理某值的reduce灰常耗時
Count Distinct
某特殊值過多
處理此特殊值的reduce耗時
1.2原因:
1)攀唯、key分布不均勻
2)洁桌、業(yè)務數(shù)據(jù)本身的特性
3)、建表時考慮不周
4)侯嘀、某些SQL語句本身就有數(shù)據(jù)傾斜
1.3表現(xiàn):
任務進度長時間維持在99%(或100%)另凌,查看任務監(jiān)控頁面,發(fā)現(xiàn)只有少量(1個或幾個)reduce子任務未完成戒幔。因為其處理的數(shù)據(jù)量和其他reduce差異過大吠谢。
單一reduce的記錄數(shù)與平均記錄數(shù)差異過大,通呈ィ可能達到3倍甚至更多工坊。 最長時長遠大于平均時長。
2數(shù)據(jù)傾斜的解決方案
2.1參數(shù)調節(jié):
hive.map.aggr = true
Map 端部分聚合敢订,相當于Combiner
hive.groupby.skewindata=true
有數(shù)據(jù)傾斜的時候進行負載均衡王污,當選項設定為 true,生成的查詢計劃會有兩個 MR Job楚午。第一個 MR Job 中玉掸,Map 的輸出結果集合會隨機分布到 Reduce 中,每個 Reduce 做部分聚合操作醒叁,并輸出結果,這樣處理的結果是相同的 Group By Key 有可能被分發(fā)到不同的 Reduce 中泊业,從而達到負載均衡的目的把沼;第二個 MR Job 再根據(jù)預處理的數(shù)據(jù)結果按照 Group By Key 分布到 Reduce 中(這個過程可以保證相同的 Group By Key 被分布到同一個 Reduce 中),最后完成最終的聚合操作吁伺。
2.2 SQL語句調節(jié):
如何Join:
關于驅動表的選取饮睬,選用join key分布最均勻的表作為驅動表
做好列裁剪和filter操作,以達到兩表做join的時候篮奄,數(shù)據(jù)量相對變小的效果捆愁。
大小表Join:
使用map join讓小的維度表(1000條以下的記錄條數(shù)) 先進內存。在map端完成reduce.
大表Join大表:
把空值的key變成一個字符串加上隨機數(shù)窟却,把傾斜的數(shù)據(jù)分到不同的reduce上昼丑,由于null值關聯(lián)不上,處理后并不影響最終結果夸赫。
count distinct大量相同特殊值
count distinct時菩帝,將值為空的情況單獨處理,如果是計算count distinct,可以不用處理呼奢,直接過濾宜雀,在最后結果中加1。如果還有其他計算握础,需要進行group by辐董,可以先將值為空的記錄單獨處理,再和其他計算結果進行union禀综。
group by維度過屑蚝妗:
采用sum() group by的方式來替換count(distinct)完成計算。
特殊情況特殊處理:
在業(yè)務邏輯優(yōu)化效果的不大情況下菇存,有些時候是可以將傾斜的數(shù)據(jù)單獨拿出來處理夸研。最后union回去。
3典型的業(yè)務場景
3.1空值產(chǎn)生的數(shù)據(jù)傾斜
場景:如日志中依鸥,常會有信息丟失的問題亥至,比如日志中的 user_id,如果取其中的 user_id 和 用戶表中的user_id 關聯(lián)贱迟,會碰到數(shù)據(jù)傾斜的問題姐扮。
解決方法1: user_id為空的不參與關聯(lián)(紅色字體為修改后)
select * from log a
join users b
on a.user_id is not null
and a.user_id = b.user_id
union all
select * from log a
where a.user_id is null;
解決方法2 :賦與空值分新的key值
select *
from log a
left outer join users b
on case when a.user_id is null then concat(‘hive’,rand() ) else a.user_id end = b.user_id;
結論:方法2比方法1效率更好,不但io少了衣吠,而且作業(yè)數(shù)也少了茶敏。解決方法1中 log讀取兩次,jobs是2缚俏。解決方法2 job數(shù)是1 惊搏。這個優(yōu)化適合無效 id (比如 -99 , ’’, null 等) 產(chǎn)生的傾斜問題。把空值的 key 變成一個字符串加上隨機數(shù)忧换,就能把傾斜的數(shù)據(jù)分到不同的reduce上 ,解決數(shù)據(jù)傾斜問題恬惯。
3.2不同數(shù)據(jù)類型關聯(lián)產(chǎn)生數(shù)據(jù)傾斜
場景:用戶表中user_id字段為int,log表中user_id字段既有string類型也有int類型亚茬。當按照user_id進行兩個表的Join操作時酪耳,默認的Hash操作會按int型的id來進行分配,這樣會導致所有string類型id的記錄都分配到一個Reducer中刹缝。
解決方法:把數(shù)字類型轉換成字符串類型
select * from users a
left outer join logs b
on a.usr_id = cast(b.user_id as string)
3.3小表不小不大碗暗,怎么用 map join 解決傾斜問題
使用 map join 解決小表(記錄數(shù)少)關聯(lián)大表的數(shù)據(jù)傾斜問題,這個方法使用的頻率非常高梢夯,但如果小表很大言疗,大到map join會出現(xiàn)bug或異常,這時就需要特別的處理厨疙。 以下例子:
select * from log a
left outer join users b
on a.user_id = b.user_id;
users 表有 600w+ 的記錄洲守,把 users 分發(fā)到所有的 map 上也是個不小的開銷疑务,而且 map join 不支持這么大的小表。如果用普通的 join梗醇,又會碰到數(shù)據(jù)傾斜的問題知允。
解決方法:
select /+mapjoin(x)/* from log a
left outer join (
select /+mapjoin(c)/d.*
from ( select distinct user_id from log ) c
join users d
on c.user_id = d.user_id
) x
on a.user_id = b.user_id;
假如,log里user_id有上百萬個叙谨,這就又回到原來map join問題温鸽。所幸,每日的會員uv不會太多手负,有交易的會員不會太多涤垫,有點擊的會員不會太多,有傭金的會員不會太多等等竟终。所以這個方法能解決很多場景下的數(shù)據(jù)傾斜問題蝠猬。
4總結
使map的輸出數(shù)據(jù)更均勻的分布到reduce中去,是我們的最終目標统捶。由于Hash算法的局限性榆芦,按key Hash會或多或少的造成數(shù)據(jù)傾斜。大量經(jīng)驗表明數(shù)據(jù)傾斜的原因是人為的建表疏忽或業(yè)務邏輯可以規(guī)避的喘鸟。在此給出較為通用的步驟:
1匆绣、采樣log表,哪些user_id比較傾斜什黑,得到一個結果表tmp1崎淳。由于對計算框架來說,所有的數(shù)據(jù)過來愕把,他都是不知道數(shù)據(jù)分布情況的拣凹,所以采樣是并不可少的。
2恨豁、數(shù)據(jù)的分布符合社會學統(tǒng)計規(guī)則咐鹤,貧富不均。傾斜的key不會太多圣絮,就像一個社會的富人不多,奇特的人不多一樣雕旨。所以tmp1記錄數(shù)會很少扮匠。把tmp1和users做map join生成tmp2,把tmp2讀到distribute file cache。這是一個map過程凡涩。
3棒搜、map讀入users和log,假如記錄來自log,則檢查user_id是否在tmp2里活箕,如果是力麸,輸出到本地文件a,否則生成<user_id,value>的key,value對,假如記錄來自member,生成<user_id,value>的key,value對,進入reduce階段克蚂。
4闺鲸、最終把a文件,把Stage3 reduce階段輸出的文件合并起寫到hdfs埃叭。
如果確認業(yè)務需要這樣傾斜的邏輯摸恍,考慮以下的優(yōu)化方案:
1、對于join赤屋,在判斷小表不大于1G的情況下立镶,使用map join
2、對于group by或distinct类早,設定 hive.groupby.skewindata=true
3媚媒、盡量使用上述的SQL語句調節(jié)進行優(yōu)化