關鍵詞 算法 |
情形 負載均衡 |
後果 框架 |
Join 字體 |
其中一個表較小, 優化 可是key集中 spa |
分發到某一個或幾個Reduce上的數據遠高於平均值 日誌 |
大表與大表,可是分桶的判斷字段0值或空值過多 code |
這些空值都由一個reduce處理,灰常慢 orm |
|
group by 內存 |
group by 維度太小, 某值的數量過多 |
處理某值的reduce灰常耗時 |
Count Distinct |
某特殊值過多 |
處理此特殊值的reduce耗時 |
1)、key分佈不均勻
2)、業務數據自己的特性
3)、建表時考慮不周
4)、某些SQL語句自己就有數據傾斜
任務進度長時間維持在99%(或100%),查看任務監控頁面,發現只有少許(1個或幾個)reduce子任務未完成。由於其處理的數據量和其餘reduce差別過大。
單一reduce的記錄數與平均記錄數差別過大,一般可能達到3倍甚至更多。 最長時長遠大於平均時長。
2.1參數調節:
hive.map.aggr = true
Map 端部分聚合,至關於Combiner
hive.groupby.skewindata=true
有數據傾斜的時候進行負載均衡,當選項設定爲 true,生成的查詢計劃會有兩個 MR Job。第一個 MR Job 中,Map 的輸出結果集合會隨機分佈到 Reduce 中,每一個 Reduce 作部分聚合操做,並輸出結果,這樣處理的結果是相同的 Group By Key 有可能被分發到不一樣的 Reduce 中,從而達到負載均衡的目的;第二個 MR Job 再根據預處理的數據結果按照 Group By Key 分佈到 Reduce 中(這個過程能夠保證相同的 Group By Key 被分佈到同一個 Reduce 中),最後完成最終的聚合操做。
2.2 SQL語句調節:
如何Join:
關於驅動表的選取,選用join key分佈最均勻的表做爲驅動表
作好列裁剪和filter操做,以達到兩表作join的時候,數據量相對變小的效果。
大小表Join:
使用map join讓小的維度表(1000條如下的記錄條數) 先進內存。在map端完成reduce.
大表Join大表:
把空值的key變成一個字符串加上隨機數,把傾斜的數據分到不一樣的reduce上,因爲null值關聯不上,處理後並不影響最終結果。
count distinct大量相同特殊值
count distinct時,將值爲空的狀況單獨處理,若是是計算count distinct,能夠不用處理,直接過濾,在最後結果中加1。若是還有其餘計算,須要進行group by,能夠先將值爲空的記錄單獨處理,再和其餘計算結果進行union。
group by維度太小:
採用sum() group by的方式來替換count(distinct)完成計算。
特殊狀況特殊處理:
在業務邏輯優化效果的不大狀況下,有些時候是能夠將傾斜的數據單獨拿出來處理。最後union回去。
場景:如日誌中,常會有信息丟失的問題,好比日誌中的 user_id,若是取其中的 user_id 和 用戶表中的user_id 關聯,會碰到數據傾斜的問題。
解決方法1: user_id爲空的不參與關聯(紅色字體爲修改後)
在業務邏輯優化效果的不大狀況下,有些時候是能夠將傾斜的數據單獨拿出來處理。最後union回去。
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少了,並且做業數也少了。解決方法1中 log讀取兩次,jobs是2。解決方法2 job數是1 。這個優化適合無效 id (好比 -99 , ’’, null 等) 產生的傾斜問題。把空值的 key 變成一個字符串加上隨機數,就能把傾斜的數據分到不一樣的reduce上 ,解決數據傾斜問題。
場景:用戶表中user_id字段爲int,log表中user_id字段既有string類型也有int類型。當按照user_id進行兩個表的Join操做時,默認的Hash操做會按int型的id來進行分配,這樣會致使全部string類型id的記錄都分配到一個Reducer中。
解決方法:把數字類型轉換成字符串類型
select * from users a left outer join logs b on a.usr_id = cast(b.user_id as string)
|
使用 map join 解決小表(記錄數少)關聯大表的數據傾斜問題,這個方法使用的頻率很是高,但若是小表很大,大到map join會出現bug或異常,這時就須要特別的處理。 如下例子:
select * from log a left outer join users b on a.user_id = b.user_id;
|
users 表有 600w+ 的記錄,把 users 分發到全部的 map 上也是個不小的開銷,並且 map join 不支持這麼大的小表。若是用普通的 join,又會碰到數據傾斜的問題。
解決方法:
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不會太多,有交易的會員不會太多,有點擊的會員不會太多,有佣金的會員不會太多等等。因此這個方法能解決不少場景下的數據傾斜問題。
使map的輸出數據更均勻的分佈到reduce中去,是咱們的最終目標。因爲Hash算法的侷限性,按key Hash會或多或少的形成數據傾斜。大量經驗代表數據傾斜的緣由是人爲的建表疏忽或業務邏輯能夠規避的。在此給出較爲通用的步驟:
一、採樣log表,哪些user_id比較傾斜,獲得一個結果表tmp1。因爲對計算框架來講,全部的數據過來,他都是不知道數據分佈狀況的,因此採樣是並不可少的。
二、數據的分佈符合社會學統計規則,貧富不均。傾斜的key不會太多,就像一個社會的富人很少,奇特的人很少同樣。因此tmp1記錄數會不多。把tmp1和users作map join生成tmp2,把tmp2讀到distribute file cache。這是一個map過程。
三、map讀入users和log,假如記錄來自log,則檢查user_id是否在tmp2裏,若是是,輸出到本地文件a,不然生成<user_id,value>的key,value對,假如記錄來自member,生成<user_id,value>的key,value對,進入reduce階段。
四、最終把a文件,把Stage3 reduce階段輸出的文件合併起寫到hdfs。
若是確認業務須要這樣傾斜的邏輯,考慮如下的優化方案:
一、對於join,在判斷小表不大於1G的狀況下,使用map join
二、對於group by或distinct,設定 hive.groupby.skewindata=true
三、儘可能使用上述的SQL語句調節進行優化