這篇文章主要介紹了Hive中SQL數(shù)據(jù)傾斜及優(yōu)化的示例分析,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
網(wǎng)站建設(shè)公司,為您提供網(wǎng)站建設(shè),網(wǎng)站制作,網(wǎng)頁設(shè)計及定制網(wǎng)站建設(shè)服務(wù),專注于成都企業(yè)網(wǎng)站建設(shè),高端網(wǎng)頁制作,對OPP膠袋等多個行業(yè)擁有豐富的網(wǎng)站建設(shè)經(jīng)驗的網(wǎng)站建設(shè)公司。專業(yè)網(wǎng)站設(shè)計,網(wǎng)站優(yōu)化推廣哪家好,專業(yè)網(wǎng)站推廣優(yōu)化,H5建站,響應(yīng)式網(wǎng)站。
關(guān)鍵詞 | 情形 | 后果 |
Join | 其中一個表較小, 但是key集中 | 分發(fā)到某一個或幾個Reduce上的數(shù)據(jù)遠高于平均值 |
大表與大表,但是分桶的判斷字段0值或空值過多 | 這些空值都由一個reduce處理,灰常慢 | |
group by | group by 維度過小, 某值的數(shù)量過多 | 處理某值的reduce灰常耗時 |
Count Distinct | 某特殊值過多 | 處理此特殊值的reduce耗時 |
1)、key分布不均勻
2)、業(yè)務(wù)數(shù)據(jù)本身的特性
3)、建表時考慮不周
4)、某些SQL語句本身就有數(shù)據(jù)傾斜
任務(wù)進度長時間維持在99%(或100%),查看任務(wù)監(jiān)控頁面,發(fā)現(xiàn)只有少量(1個或幾個)reduce子任務(wù)未完成。因為其處理的數(shù)據(jù)量和其他reduce差異過大。
單一reduce的記錄數(shù)與平均記錄數(shù)差異過大,通??赡苓_到3倍甚至更多。 最長時長遠大于平均時長。
hive.map.aggr=true
Map 端部分聚合,相當于Combiner
hive.groupby.skewindata=true
有數(shù)據(jù)傾斜的時候進行負載均衡,當選項設(shè)定為 true,生成的查詢計劃會有兩個 MR Job。第一個 MR Job 中,Map 的輸出結(jié)果集合會隨機分布到 Reduce 中,每個 Reduce 做部分聚合操作,并輸出結(jié)果,這樣處理的結(jié)果是相同的 Group By Key 有可能被分發(fā)到不同的 Reduce 中,從而達到負載均衡的目的;第二個 MR Job 再根據(jù)預(yù)處理的數(shù)據(jù)結(jié)果按照 Group By Key 分布到 Reduce 中(這個過程可以保證相同的 Group By Key 被分布到同一個 Reduce 中),最后完成最終的聚合操作。
如何Join:
關(guān)于驅(qū)動表的選取,選用join key分布最均勻的表作為驅(qū)動表
做好列裁剪和filter操作,以達到兩表做join的時候,數(shù)據(jù)量相對變小的效果。
大小表Join:
使用map join讓小的維度表(1000條以下的記錄條數(shù)) 先進內(nèi)存。在map端完成reduce.
大表Join大表:
把空值的key變成一個字符串加上隨機數(shù),把傾斜的數(shù)據(jù)分到不同的reduce上,由于null值關(guān)聯(lián)不上,處理后并不影響最終結(jié)果。
count distinct大量相同特殊值
count distinct時,將值為空的情況單獨處理,如果是計算count distinct,可以不用處理,直接過濾,在最后結(jié)果中加1。如果還有其他計算,需要進行g(shù)roup by,可以先將值為空的記錄單獨處理,再和其他計算結(jié)果進行union。
group by維度過?。?/strong>
采用sum() group by的方式來替換count(distinct)完成計算。
特殊情況特殊處理:
在業(yè)務(wù)邏輯優(yōu)化效果的不大情況下,有些時候是可以將傾斜的數(shù)據(jù)單獨拿出來處理。最后union回去。
場景:如日志中,常會有信息丟失的問題,比如日志中的 user_id,如果取其中的 user_id 和 用戶表中的user_id 關(guān)聯(lián),會碰到數(shù)據(jù)傾斜的問題。
解決方法1: user_id為空的不參與關(guān)聯(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;
結(jié)論:方法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ù)傾斜問題。
場景:用戶表中user_id字段為int,log表中user_id字段既有string類型也有int類型。當按照user_id進行兩個表的Join操作時,默認的Hash操作會按int型的id來進行分配,這樣會導致所有string類型id的記錄都分配到一個Reducer中。
解決方法:把數(shù)字類型轉(zhuǎn)換成字符串類型
select * from users a left outer join logs b on a.usr_id = cast(b.user_id as string)
使用 map join 解決小表(記錄數(shù)少)關(guān)聯(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ù)傾斜問題。
計算 uv 的時候,經(jīng)常會用到 COUNT(DISTINCT),但在數(shù)據(jù)比較傾斜的時候 COUNT(DISTINCT) 會比較慢。這時可以嘗試用 GROUP BY 改寫代碼計算 uv。
INSERT OVERWRITE TABLE s_dw_tanx_adzone_uv PARTITION (ds=20120329)
SELECT 20120329 AS thedate,adzoneid,COUNT(DISTINCT acookie) AS uv FROM s_ods_log_tanx_pv t WHERE t.ds=20120329 GROUP BY adzoneid
關(guān)于COUNT(DISTINCT)的數(shù)據(jù)傾斜問題不能一概而論,要依情況而定,下面是我測試的一組數(shù)據(jù):
測試數(shù)據(jù):169857條
#統(tǒng)計每日IP CREATE TABLE ip_2014_12_29 AS SELECT COUNT(DISTINCT ip) AS IP FROM logdfs WHERE logdate='2014_12_29'; 耗時:24.805 seconds #統(tǒng)計每日IP(改造) CREATE TABLE ip_2014_12_29 AS SELECT COUNT(1) AS IP FROM (SELECT DISTINCT ip from logdfs WHERE logdate='2014_12_29') tmp; 耗時:46.833 seconds
測試結(jié)果表名:明顯改造后的語句比之前耗時,這是因為改造后的語句有2個SELECT,多了一個job,這樣在數(shù)據(jù)量小的時候,數(shù)據(jù)不會存在傾斜問題。
Hive 對 union all 的優(yōu)化的特性:對 union all 優(yōu)化只局限于非嵌套查詢。
消滅子查詢內(nèi)的 group by
示例 1:子查詢內(nèi)有 group by
SELECT * FROM (SELECT * FROM t1 GROUP BY c1,c2,c3 UNION ALL SELECT * FROM t2 GROUP BY c1,c2,c3)t3 GROUP BY c1,c2,c3
從業(yè)務(wù)邏輯上說,子查詢內(nèi)的 GROUP BY 怎么都看顯得多余(功能上的多余,除非有 COUNT(DISTINCT)),如果不是因為 Hive Bug 或者性能上的考量(曾經(jīng)出現(xiàn)如果不執(zhí)行子查詢 GROUP BY,數(shù)據(jù)得不到正確的結(jié)果的 Hive Bug)。所以這個 Hive 按經(jīng)驗轉(zhuǎn)換成如下所示:
SELECT * FROM (SELECT * FROM t1 UNION ALL SELECT * FROM t2)t3 GROUP BY c1,c2,c3
調(diào)優(yōu)結(jié)果:經(jīng)過測試,并未出現(xiàn) union all 的 Hive Bug,數(shù)據(jù)是一致的。MapReduce 的 作業(yè)數(shù)由 3 減少到 1。
t1 相當于一個目錄,t2 相當于一個目錄,對 Map/Reduce 程序來說,t1,t2 可以作為 Map/Reduce 作業(yè)的 mutli inputs。這可以通過一個 Map/Reduce 來解決這個問題。Hadoop 的 計算框架,不怕數(shù)據(jù)多,就怕作業(yè)數(shù)多。
但如果換成是其他計算平臺如 Oracle,那就不一定了,因為把大的輸入拆成兩個輸入, 分別排序匯總后 merge(假如兩個子排序是并行的話),是有可能性能更優(yōu)的(比如希爾排 序比冒泡排序的性能更優(yōu))。
消滅子查詢內(nèi)的 COUNT(DISTINCT),MAX,MIN。
SELECT * FROM (SELECT * FROM t1 UNION ALL SELECT c1,c2,c3 COUNT(DISTINCT c4) FROM t2 GROUP BY c1,c2,c3) t3 GROUP BY c1,c2,c3;
由于子查詢里頭有 COUNT(DISTINCT)操作,直接去 GROUP BY 將達不到業(yè)務(wù)目標。這時采用 臨時表消滅 COUNT(DISTINCT)作業(yè)不但能解決傾斜問題,還能有效減少 jobs。
INSERT t4 SELECT c1,c2,c3,c4 FROM t2 GROUP BY c1,c2,c3; SELECT c1,c2,c3,SUM(income),SUM(uv) FROM (SELECT c1,c2,c3,income,0 AS uv FROM t1 UNION ALL SELECT c1,c2,c3,0 AS income,1 AS uv FROM t2) t3 GROUP BY c1,c2,c3;
job 數(shù)是 2,減少一半,而且兩次 Map/Reduce 比 COUNT(DISTINCT)效率更高。
調(diào)優(yōu)結(jié)果:千萬級別的類目表,member 表,與 10 億級得商品表關(guān)聯(lián)。原先 1963s 的任務(wù)經(jīng)過調(diào)整,1152s 即完成。
消滅子查詢內(nèi)的 JOIN
SELECT * FROM (SELECT * FROM t1 UNION ALL SELECT * FROM t4 UNION ALL SELECT * FROM t2 JOIN t3 ON t2.id=t3.id) x GROUP BY c1,c2;
上面代碼運行會有 5 個 jobs。加入先 JOIN 生存臨時表的話 t5,然后 UNION ALL,會變成 2 個 jobs。
INSERT OVERWRITE TABLE t5 SELECT * FROM t2 JOIN t3 ON t2.id=t3.id; SELECT * FROM (t1 UNION ALL t4 UNION ALL t5);
調(diào)優(yōu)結(jié)果顯示:針對千萬級別的廣告位表,由原先 5 個 Job 共 15 分鐘,分解為 2 個 job 一個 8-10 分鐘,一個3分鐘。
使map的輸出數(shù)據(jù)更均勻的分布到reduce中去,是我們的最終目標。由于Hash算法的局限性,按key Hash會或多或少的造成數(shù)據(jù)傾斜。大量經(jīng)驗表明數(shù)據(jù)傾斜的原因是人為的建表疏忽或業(yè)務(wù)邏輯可以規(guī)避的。在此給出較為通用的步驟:
1、采樣log表,哪些user_id比較傾斜,得到一個結(jié)果表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,否則生成
4、最終把a文件,把Stage3 reduce階段輸出的文件合并起寫到hdfs。
如果確認業(yè)務(wù)需要這樣傾斜的邏輯,考慮以下的優(yōu)化方案:
1、對于join,在判斷小表不大于1G的情況下,使用map join
2、對于group by或distinct,設(shè)定 hive.groupby.skewindata=true
3、盡量使用上述的SQL語句調(diào)節(jié)進行優(yōu)化
hadoop處理數(shù)據(jù)的過程,有幾個顯著的特征:
不怕數(shù)據(jù)多,就怕數(shù)據(jù)傾斜。
對jobs數(shù)比較多的作業(yè)運行效率相對比較低,比如即使有幾百行的表,如果多次關(guān)聯(lián)多次匯總,產(chǎn)生十幾個jobs,沒半小時是跑不完的。map reduce作業(yè)初始化的時間是比較長的。
對sum,count來說,不存在數(shù)據(jù)傾斜問題。
對count(distinct ),效率較低,數(shù)據(jù)量一多,準出問題,如果是多count(distinct )效率更低。
優(yōu)化可以從幾個方面著手:
好的模型設(shè)計事半功倍。
解決數(shù)據(jù)傾斜問題。
減少job數(shù)。
設(shè)置合理的map reduce的task數(shù),能有效提升性能。(比如,10w+級別的計算,用160個reduce,那是相當?shù)睦速M,1個足夠)。
自己動手寫sql解決數(shù)據(jù)傾斜問題是個不錯的選擇。set hive.groupby.skewindata=true;這是通用的算法優(yōu)化,但算法優(yōu)化總是漠視業(yè)務(wù),習慣性提供通用的解決方法。 Etl開發(fā)人員更了解業(yè)務(wù),更了解數(shù)據(jù),所以通過業(yè)務(wù)邏輯解決傾斜的方法往往更精確,更有效。
對count(distinct)采取漠視的方法,尤其數(shù)據(jù)大的時候很容易產(chǎn)生傾斜問題,不抱僥幸心理。自己動手,豐衣足食。
對小文件進行合并,是行至有效的提高調(diào)度效率的方法,假如我們的作業(yè)設(shè)置合理的文件數(shù),對云梯的整體調(diào)度效率也會產(chǎn)生積極的影響。
優(yōu)化時把握整體,單個作業(yè)最優(yōu)不如整體最優(yōu)。
細節(jié)上就是:
去除查詢中不需要的column
Where條件判斷等在TableScan階段就進行過濾
利用Partition信息,只讀取符合條件的Partition
Map端join,以大表作驅(qū)動,小表載入所有mapper內(nèi)存中
調(diào)整Join順序,確保以大表作為驅(qū)動表
對于數(shù)據(jù)分布不均衡的表Group by時,為避免數(shù)據(jù)集中到少數(shù)的reducer上,分成兩個map-reduce階段。第一個階段先用Distinct列進行shuffle,然后在reduce端部分聚合,減小數(shù)據(jù)規(guī)模,第二個map-reduce階段再按group-by列聚合。
在map端用hash進行部分聚合,減小reduce端數(shù)據(jù)處理規(guī)模。
感謝你能夠認真閱讀完這篇文章,希望小編分享的“Hive中SQL數(shù)據(jù)傾斜及優(yōu)化的示例分析”這篇文章對大家有幫助,同時也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,更多相關(guān)知識等著你來學習!