1. 程式人生 > >hive深度理解與調優

hive深度理解與調優

參考一些檔案弄過來。。。。。讀了一遍,沒有完全記住。呵呵,留著以後當手冊用,沒事兒來翻。

8.1 針對每次簡單查詢都會使用mapreduce,設定hiveconf hive.fetch.task.conversion=more。通過Fetch 獲取資料,不再經過mapreduce

8.2 
第一部分:Hadoop 計算框架的特性
什麼是資料傾斜
•由於資料的不均衡原因,導致資料分佈不均勻,造成資料大量的集中到一點,造成資料熱點
Hadoop框架的特性
•不怕資料大,怕資料傾斜
•jobs數比較多的作業執行效率相對比較低,比如即使有幾百行的表,如果多次關聯多次彙總,產生十幾個jobs,耗時很長。原因是map reduce作業初始化的時間是比較長的
•sum,count,max,min等UDAF,不怕資料傾斜問題,hadoop在map端的彙總合併優化,使資料傾斜不成問題
•count(distinct ),在資料量大的情況下,效率較低,因為count(distinct)是按group by分組,按distinct欄位排序,一般這種分佈方式是很傾斜的
第二部分:優化的常用手段
優化的常用手段
•解決資料傾斜問題
•減少job數
•設定合理的map reduce的task數,能有效提升效能。
•瞭解資料分佈,自己動手解決資料傾斜問題是個不錯的選擇
•資料量較大的情況下,慎用count(distinct)。
•對小檔案進行合併,是行至有效的提高排程效率的方法。
•優化時把握整體,單個作業最優不如整體最優。
第三部分:Hive的資料型別方面的優化
優化原則
•按照一定規則分割槽(例如根據日期)。通過分割槽,查詢的時候指定分割槽,會大大減少在無用資料上的掃描, 同時也非常方便資料清理。
•合理的設定Buckets。在一些大資料join的情況下,map join有時候會記憶體不夠。如果使用Bucket Map Join的話,可以只把其中的一個bucket放到記憶體中,記憶體中原來放不下的記憶體表就變得可以放下。這需要使用buckets的鍵進行join的條件連結,並且需要如下設定
     set hive.optimize.bucketmapjoin = true
 第四部分:Hive的操作方面的優化
•全排序
#Hive的排序關鍵字是SORT BY,它有意區別於傳統資料庫的ORDER BY也是為了強調兩者的區別–SORT BY只能在單機範圍內排序
不分發資料,使用單個reducer:set mapred.reduce.tasks=1
這一方法的缺陷在於reduce端成為了效能瓶頸,而且在資料量大的情況下一般都無法得到結果。但是實踐中這仍然是最常用的方法,原因是通常排序的查詢是為了得到排名靠前的若干結果,因此可以用limit子句大大減少資料量。使用limit n後,傳輸到reduce端(單機)的資料記錄數就減少到n* (map個數)。
•怎樣做笛卡爾積
#當Hive設定為嚴格模式(hive.mapred.mode=strict)時,不允許在HQL語句中出現笛卡爾積
#MapJoin是的解決辦法,MapJoin,顧名思義,會在Map端完成Join操作。這需要將Join操作的一個或多個表完全讀入記憶體。
MapJoin的用法是在查詢/子查詢的SELECT關鍵字後面新增/*+ MAPJOIN(tablelist)*/提示優化器轉化為MapJoin(目前Hive的優化器不能自動優化MapJoin)
#其中tablelist可以是一個表,或以逗號連線的表的列表。tablelist中的表將會讀入記憶體,應該將小表寫在這裡
#在大表和小表做笛卡爾積時,規避笛卡爾積的方法是,給Join新增一個Join key,原理很簡單:將小表擴充一列join key,並將小表的條目複製數倍,join key各不相同;將大表擴充一列join key為隨機數
•怎樣決定map個數
通常情況下,作業會通過input的目錄產生一個或者多個map任務
主要的決定因素有: input的檔案總個數,input的檔案大小,叢集設定的檔案塊大小(目前為128M, 可在hive中通過set dfs.block.size;命令檢視到,該引數不能自定義修改)
#是不是map數越多越好
答案是否定的。如果一個任務有很多小檔案(遠遠小於塊大小128m),則每個小檔案也會被當做一個塊,用一個map任務來完成, 
而一個map任務啟動和初始化的時間遠遠大於邏輯處理的時間,就會造成很大的資源浪費。 
而且,同時可執行的map數是受限的
#是不是保證每個map處理接近128m的檔案塊,就高枕無憂了?
   答案也是不一定。比如有一個127m的檔案,正常會用一個map去完成,但這個檔案只有一個或者兩個小欄位,卻有幾千萬的記錄, 
#如果map處理的邏輯比較複雜,用一個map任務去做,肯定也比較耗時。
#針對上面的問題3和4,我們需要採取兩種方式來解決:即減少map數和增加map數;
---舉例---
    a) 假設input目錄下有1個檔案a,大小為780M,那麼hadoop會將該檔案a分隔成7個塊(6個128m的塊和1個12m的塊),從而產生7個map數 
b)   假設input目錄下有3個檔案a,b,c,大小分別為10m,20m,130m,那麼hadoop會分隔成4個塊(10m,20m,128m,2m),從而產生4個map數 
即,如果檔案大於塊大小(128m),那麼會拆分,如果小於塊大小,則把該檔案當成一個塊。
•怎樣決定reducer個數
#Hadoop MapReduce程式中,reducer個數的設定極大影響執行效率
#不指定reducer個數的情況下,Hive會猜測確定一個reducer個數,基於以下兩個設定:
    引數1:hive.exec.reducers.bytes.per.reducer(預設為1G)
   引數2 :hive.exec.reducers.max(預設為999)
#計算reducer數的公式:N=min(引數2,總輸入資料量/引數1)
#依據Hadoop的經驗,可以將引數2設定為0.95*(叢集中TaskTracker個數)
#reduce個數並不是越多越好
同map一樣,啟動和初始化reduce也會消耗時間和資源; 
另外,有多少個reduce,就會有多少個輸出檔案,如果生成了很多個小檔案,那麼如果這些小檔案作為下一個任務的輸入,則也會出現小檔案過多的問題
#什麼情況下只有一個reduce
   很多時候你會發現任務中不管資料量多大,不管你有沒有設定調整reduce個數的引數,任務中一直都只有一個reduce任務; 
其實只有一個reduce任務的情況,除了資料量小於hive.exec.reducers.bytes.per.reducer引數值的情況外,還有以下原因: 
a)    沒有group by的彙總 
b)    用了Order by
•合併MapReduce操作
a #Multi-group by
是Hive的一個非常好的特性,它使得Hive中利用中間結果變得非常方便
FROM (SELECT a.status, b.school, b.gender  
FROM status_updates a JOIN profiles b  
ON (a.userid = b.userid and  a.ds='2009-03-20' )  
) subq1  
INSERT OVERWRITE TABLE gender_summary  PARTITION(ds='2009-03-20')  
SELECT subq1.gender, COUNT(1) GROUP BY subq1.gender  
INSERT OVERWRITE TABLE school_summary  PARTITION(ds='2009-03-20')  
SELECT subq1.school, COUNT(1) GROUP BY subq1.school
上述查詢語句使用了Multi-group by特性連續group by了2次資料,使用不同的group by key。這一特性可以減少一次MapReduce操作。
b Multi-distinct
Multi-distinct是淘寶開發的另一個multi-xxx特性,使用Multi-distinct可以在同一查詢/子查詢中使用多個distinct,這同樣減少了多次MapReduce操作。
•Bucket 與 sampling
#Bucket是指將資料以指定列的值為key進行hash,hash到指定數目的桶中。這樣就可以支援高效取樣了
#Sampling可以在全體資料上進行取樣,這樣效率自然就低,它還是要去訪問所有資料。而如果一個表已經對某一列製作了bucket,就可以取樣所有桶中指定序號的某個桶,這就減少了訪問量。
#如下例所示就是取樣了test中32個桶中的第三個桶。
#SELECT * FROM test TABLESAMPLE(BUCKET 3 OUT OF 32);
•Partition
#在使用寫有 Join 操作的查詢語句時有一條原則:應該將條目少的表/子查詢放在 Join 操作符的左邊
#原因是在 Join 操作的 Reduce階段,位於Join操作符左邊的表的內容會被載入進記憶體,將條目少的表放在左邊,可以有效減少發生 OOM錯誤的機率
•JOIN
#Join 操作在 Map 階段完成,不再需要Reduce,前提條件是需要的資料在 Map的過程中可以訪問到
#例如:
#INSERT OVERWRITE TABLE phone_traffic
SELECT /*+ MAPJOIN(phone_location) */  l.phone,p.location,l.traffic from phone_location p join log l on (p.phone=l.phone)
#相關的引數為:
hive.join.emit.interval = 1000 //How many rows in the right-most join operand Hive should buffer before emitting the join result.
hive.mapjoin.size.key = 10000
hive.mapjoin.cache.numrows = 10000
•Group By
#Map 端部分聚合
#並不是所有的聚合操作都需要在 Reduce 端完成,很多聚合操作都可以先在 Map 端進行部分聚合,最後在 Reduce 端得出最終結果
 
# 基於 Hash
# 引數包括:
#hive.map.aggr = true 是否在 Map 端進行聚合,預設為 True
#hive.groupby.mapaggr.checkinterval = 100000 在 Map 端進行聚合操作的條目數目
#有資料傾斜的時候進行負載均衡
#hive.groupby.skewindata = false
#當選項設定為 true,生成的查詢計劃會有兩個 MR Job。第一個 MR Job 中,Map 的輸出結果集合會隨機分佈到 Reduce 中,每個 Reduce 做部分聚合操作,並輸出結果,這樣處理的結果是相同的 Group By Key 有可能被分發到不同的 Reduce 中,從而達到負載均衡的目的;第二個 MR Job 再根據預處理的資料結果按照 Group By Key 分佈到 Reduce 中(這個過程可以保證相同的 Group By Key 被分佈到同一個 Reduce 中),最後完成最終的聚合操作。
•合併小檔案
•檔案數目過多,會給 HDFS 帶來壓力,並且會影響處理效率,可以通過合併 Map 和 Reduce 的結果檔案來消除這樣的影響:
•hive.merge.mapfiles = true 是否和並 Map 輸出檔案,預設為 True
•hive.merge.mapredfiles = false 是否合併 Reduce 輸出檔案,預設為 False
•hive.merge.size.per.task = 256*1000*1000 合併檔案的大小
•怎樣寫exist in子句?
高效的實現是利用left semi join改寫為:
SELECT a.key, a.val  FROM a LEFT SEMI JOIN b on (a.key = b.key);
left semi join是0.5.0以上版本的特性。


csdn不會貼圖。。。。。。鬱悶中。。。。。

參考文件:
         http://sishuok.com/forum/blogPost/list/0/6229.html
         http://www.alidata.org/archives/622