1肛鹏、什么是數(shù)據(jù)傾斜逸邦?
數(shù)據(jù)分布不均勻,造成數(shù)據(jù)大量的集中到一點龄坪,造成數(shù)據(jù)熱點
2昭雌、Hadoop 框架的特性
A、不怕數(shù)據(jù)大健田,怕數(shù)據(jù)傾斜
B烛卧、Jobs 數(shù)比較多的作業(yè)運行效率相對比較低,如子查詢比較多
C、 sum,count,max,min 等聚集函數(shù)总放,通常不會有數(shù)據(jù)傾斜問題
3呈宇、主要表現(xiàn)
任務進度長時間維持在 99%或者 100%的附近,查看任務監(jiān)控頁面局雄,發(fā)現(xiàn)只有少量 reduce 子任務未完成甥啄,因為其處理的數(shù)據(jù)量和其他的 reduce 差異過大。 單一 reduce 處理的記錄數(shù)和平均記錄數(shù)相差太大炬搭,通常達到好幾倍之多蜈漓,最長時間遠大 于平均時長。
4宫盔、容易數(shù)據(jù)傾斜情況
A融虽、group by 不和聚集函數(shù)搭配使用的時候
B、count(distinct)灼芭,在數(shù)據(jù)量大的情況下有额,容易數(shù)據(jù)傾斜,因為 count(distinct)是按 group by 字段分組彼绷,按 distinct 字段排序
C巍佑、 小表關聯(lián)超大表 join
一、Map傾斜
根本原因:讀入的文件塊的數(shù)據(jù)分布不均勻寄悯。
1萤衰、上游表文件的大小不均勻,并且小文件特別多热某,導致當前表Map端讀取的數(shù)據(jù)分布不均勻腻菇,引起長尾;
- 處理方式
對上游合并小文件昔馋,同時調節(jié)本節(jié)點的小文件的參數(shù)
# 輸入合并
//CombineFileInputFormat作用:將多個小文件打包成一個InputSplit提供給一個Map處理
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
set hive.hadoop.supports.splittable.combineinputformat = true;
//執(zhí)行Map前進行小文件合并
set mapred.max.split.size=256000000;
//每個Map最大輸入大小
set mapred.min.split.size.per.node=100000000;
//一個節(jié)點上split的至少的大小
set mapred.min.split.size.per.rack=100000000;
//一個交換機下split的至少的大小
# 輸出合并
set hive.merge.mapfiles = true
//在Map-only的任務結束時合并小文件
set hive.merge.tezfiles=true;
set hive.merge.mapredfiles = true
//在Map-Reduce的任務結束時合并小文件
set hive.merge.size.per.task = 256*1000*1000
//合并文件的大小
set hive.merge.smallfiles.avgsize=16000000
//當輸出文件的平均大小小于該值時筹吐,啟動一個獨立的map-reduce任務進行文件merge
2、Map端做聚合時秘遏,由于某些Map Instance讀取文件的某個值特別多而引起長尾丘薛,主要是指Count Distinct操作。
- 處理方式
distribute by :用來控制map輸出結果的分發(fā)邦危,即map端如何拆分數(shù)據(jù)給reduce端洋侨。
通過distribute by rand() 將Map端分發(fā)后的數(shù)據(jù)重新按照隨機值再進行一次分發(fā)。使用后倦蚪,Map端只負責數(shù)據(jù)的分發(fā)希坚,不再有復雜的聚合或者笛卡爾積操作,因此不會導致Map端拖尾陵且。
二裁僧、Join傾斜
1、Join的某路輸入比較小,可以采用MapJoin聊疲,避免分發(fā)引起長尾
map join 概念:將其中做連接的小表(全量數(shù)據(jù))分發(fā)到所有 MapTask 端進行 Join茬底,從 而避免了 reduceTask,前提要求是內存足以裝下該全量數(shù)據(jù)获洲。
以大表 a 和小表 b 為例阱表,所有的 maptask 節(jié)點都裝載小表 b 的所有數(shù)據(jù),然后大表 a 的 一個數(shù)據(jù)塊數(shù)據(jù)比如說是 a1 去跟 b 全量數(shù)據(jù)做連接贡珊,就省去了 reduce 做匯總的過程最爬。 所以相對來說,在內存允許的條件下使用 map join 比直接使用 MapReduce 效率還高些飞崖, 當然這只限于做 join 查詢的時候烂叔。
- 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 優(yōu)化,由兩個參數(shù)控制:
set hive.auto.convert.join=true;
//設置 MapJoin 優(yōu)化自動開啟
set hive.mapjoin.smalltable.filesize=25000000
//設置小表不超過多大時開啟 mapjoin 優(yōu)化
2固歪、Join的每路輸入都很大,且長尾是空值導致的胯努。
場景說明:
在日志中牢裳,常會有信息丟失的問題,比如日志中的 user_id叶沛,如果取其中的 user_id 和用戶表中的 user_id 相關聯(lián)蒲讯,就會碰到數(shù)據(jù)傾斜的問題。
- 解決方案 1:user_id 為空的不參與關聯(lián)
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 少了判帮,而且作業(yè)數(shù)也少了,方案 1 中溉箕,log 表 讀了兩次晦墙,jobs 肯定是 2,而方案 2 是 1肴茄。這個優(yōu)化適合無效 id(比如-99晌畅,’’,null)產(chǎn) 生的數(shù)據(jù)傾斜寡痰,把空值的 key 變成一個字符串加上一個隨機數(shù)抗楔,就能把造成數(shù)據(jù)傾斜的 數(shù)據(jù)分到不同的 reduce 上解決數(shù)據(jù)傾斜的問題。
改變之處:使本身為 null 的所有記錄不會擁擠在同一個 reduceTask 了拦坠,會由于有替代的 隨機字符串值连躏,而分散到了多個 reduceTask 中了,由于 null 值關聯(lián)不上贞滨,處理后并不影響最終結果入热。
3、Join的每路輸入都很大,且長尾是熱點值導致的才顿,可以對熱點值和非熱點值分別進行處理莫湘,再合并數(shù)據(jù)。
具體地郑气,先將熱點key取出幅垮,對于主表數(shù)據(jù)用熱點key切分成熱點數(shù)據(jù)和非熱點數(shù)據(jù)兩部分處理,最后合并尾组。
三忙芒、Reduce傾斜
主要原因:key的數(shù)據(jù)分布不均勻
1、對同一個表按照維度對不同的列進行Count distinct操作讳侨,造成Map端數(shù)據(jù)膨脹呵萨,從而使得下游的Join和Reduce出現(xiàn)鏈路上的長尾。
2跨跨、Map端直接做聚合時出現(xiàn)key值分布不均勻潮峦,造成Reduce端長尾。
對熱點key單獨處理勇婴,再通過union all合并
3忱嘹、動態(tài)分區(qū)數(shù)過多時可能造成小文件過多,造成Reduce端長尾耕渴。
把符合不同條件的數(shù)據(jù)放到不同的分區(qū)拘悦,避免多次通過Insert Overwrite寫入表中。但是動態(tài)分區(qū)也有可能會帶來小文件過多的困擾
4橱脸、多個Distinct同時出現(xiàn)在一段SQL代碼中础米,數(shù)據(jù)會被分發(fā)多次,不僅會造成數(shù)據(jù)膨脹N倍添诉,還會把長尾現(xiàn)象放大N倍
總結:目前reduce端的數(shù)據(jù)傾斜很多是由count distinct引起的屁桑。
【參考】
Hive的數(shù)據(jù)傾斜