因爲數據分佈不均勻,形成數據大量的集中到一點,形成數據熱點框架
A、不怕數據大,怕數據傾斜函數
B、Jobs 數比較多的做業運行效率相對比較低,如子查詢比較多oop
C、 sum,count,max,min 等彙集函數,一般不會有數據傾斜問題優化
任務進度長時間維持在 99%或者 100%的附近,查看任務監控頁面,發現只有少許 reduce 子任務未完成,由於其處理的數據量和其餘的 reduce 差別過大。 單一 reduce 處理的記錄數和平均記錄數相差太大,一般達到好幾倍之多,最長時間遠大 於平均時長。spa
A、group by 不和彙集函數搭配使用的時候3d
B、count(distinct),在數據量大的狀況下,容易數據傾斜,由於 count(distinct)是按 group by 字段分組,按 distinct 字段排序日誌
C、 小表關聯超大表 joincode
A:key 分佈不均勻blog
B:業務數據自己的特性排序
C:建表考慮不周全
D:某些 HQL 語句自己就存在數據傾斜
在日誌中,常會有信息丟失的問題,好比日誌中的 user_id,若是取其中的 user_id 和用戶表中的 user_id 相關聯,就會碰到數據傾斜的問題。
解決方案 1:user_id 爲空的不參與關聯
select * from log a join user b on a.user_id is not null and a.user_id = b.user_id union all select * from log c where c.user_id is null;
解決方案 2:賦予空值新的 key 值
select * from log a left outer join user 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 是 1。這個優化適合無效 id(好比-99,’’,null)產 生的數據傾斜,把空值的 key 變
成一個字符串加上一個隨機數,就能把形成數據傾斜的 數據分到不一樣的 reduce 上解決數據傾斜的問題。
改變之處:使自己爲 null 的全部記錄不會擁擠在同一個 reduceTask 了,會因爲有替代的 隨機字符串值,而分散到了多個 reduceTask 中了,因爲 null 值關聯不上,處理後並不影響最終結果。
用戶表中 user_id 字段爲 int,log 表中 user_id 爲既有 string 也有 int 的類型, 當按照兩個表的 user_id 進行 join 操做的時候,默認的 hash 操做會按照 int 類型的 id 進 行分配,這樣就會致使全部的 string 類型的 id 就被分到同一個 reducer 當中
把數字類型 id 轉換成 string 類型的 id
select * from user a left outer join log b on b.user_id = cast(a.user_id as string)
注意:使用map join解決小表關聯大表形成的數據傾斜問題。這個方法使用的頻率很高。
map join 概念:將其中作鏈接的小表(全量數據)分發到全部 MapTask 端進行 Join,從 而避免了 reduceTask,前提要求是內存足以裝下該全量數據
以大表 a 和小表 b 爲例,全部的 maptask 節點都裝載小表 b 的全部數據,而後大表 a 的 一個數據塊數據好比說是 a1 去跟 b 全量數據作連接,就省去了 reduce 作彙總的過程。 因此相對來講,在內存容許的條件下使用 map join 比直接使用 MapReduce 效率還高些, 固然這隻限於作 join 查詢的時候。
在 hive 中,直接提供了可以在 HQL 語句指定該次查詢使用 map join,map join 的用法是 在查詢/子查詢的SELECT關鍵字後面添加/*+ MAPJOIN(tablelist) */提示優化器轉化爲map join(早期的 Hive 版本的優化器是不能自動優化 map join 的)。其中 tablelist 能夠是一個 表,或以逗號鏈接的表的列表。tablelist 中的表將會讀入內存,一般應該是將小表寫在 這裏。
MapJoin 具體用法:
select /* +mapjoin(a) */ a.id aid, name, age from a join b on a.id = b.id; select /* +mapjoin(movies) */ a.title, b.rating from movies a join ratings b on a.movieid = b.movieid;
在 hive0.11 版本之後會自動開啓 map join 優化,由兩個參數控制:
set hive.auto.convert.join=true; //設置 MapJoin 優化自動開啓
set hive.mapjoin.smalltable.filesize=25000000 //設置小表不超過多大時開啓 mapjoin 優化
若是是大大表關聯呢?那就大事化小,小事化了。把大表切分紅小表,而後分別 map join
那麼若是小表不大不小,那該如何處理呢???
使用 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 = x.user_id;
假如,log 裏 user_id 有上百萬個,這就又回到原來 map join 問題。所幸,每日的會員 uv 不會太多,有交易的會員不會太多,有點擊的會員不會太多,有佣金的會員不會太多等 等。因此這個方法能解決不少場景下的數據傾斜問題