1. 程式人生 > >hive優化之——控制hive任務中的map數和reduce數

hive優化之——控制hive任務中的map數和reduce數

計算 2-0 問題 tdi title ask hadoop 發現 http

一、 控制hive任務中的map數:

1. 通常情況下,作業會通過input的目錄產生一個或者多個map任務。
主要的決定因素有: input的文件總個數,input的文件大小,集群設置的文件塊大小(目前為128M, 可在hive中通過set dfs.block.size;命令查看到,該參數不能自定義修改);

2. 舉例:
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),那麽會拆分,如果小於塊大小,則把該文件當成一個塊。

3. 是不是map數越多越好?
答案是否定的。如果一個任務有很多小文件(遠遠小於塊大小128m),則每個小文件也會被當做一個塊,用一個map任務來完成,
而一個map任務啟動和初始化的時間遠遠大於邏輯處理的時間,就會造成很大的資源浪費。
而且,同時可執行的map數是受限的。

4. 是不是保證每個map處理接近128m的文件塊,就高枕無憂了?
答案也是不一定。比如有一個127m的文件,正常會用一個map去完成,但這個文件只有一個或者兩個小字段,卻有幾千萬的記錄,
如果map處理的邏輯比較復雜,用一個map任務去做,肯定也比較耗時。

針對上面的問題3和4,我們需要采取兩種方式來解決:即減少map數和增加map數;

如何合並小文件,減少map數?
假設一個SQL任務:
Select count(1) from popt_tbaccountcopy_mes where pt = ‘2012-07-04’;
該任務的inputdir /group/p_sdo_data/p_sdo_data_etl/pt/popt_tbaccountcopy_mes/pt=2012-07-04
共有194個文件,其中很多是遠遠小於128m的小文件,總大小9G,正常執行會用194個map任務。
Map總共消耗的計算資源: SLOTS_MILLIS_MAPS= 623,020

我通過以下方法來在map執行前合並小文件,減少map數:
set mapred.max.split.size=100000000;
set mapred.min.split.size.per.node=100000000;
set mapred.min.split.size.per.rack=100000000;
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
再執行上面的語句,用了74個map任務,map消耗的計算資源:SLOTS_MILLIS_MAPS= 333,500
對於這個簡單SQL任務,執行時間上可能差不多,但節省了一半的計算資源。
大概解釋一下,100000000表示100M, set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;這個參數表示執行前進行小文件合並,
前面三個參數確定合並文件塊的大小,大於文件塊大小128m的,按照128m來分隔,小於128m,大於100m的,按照100m來分隔,把那些小於100m的(包括小文件和分隔大文件剩下的),
進行合並,最終生成了74個塊。

如何適當的增加map數?

當input的文件都很大,任務邏輯復雜,map執行非常慢的時候,可以考慮增加Map數,來使得每個map處理的數據量減少,從而提高任務的執行效率。
假設有這樣一個任務:
Select data_desc,
count(1),
count(distinct id),
sum(case when …),
sum(case when …),
sum(…)
from a group by data_desc
如果表a只有一個文件,大小為120M,但包含幾千萬的記錄,如果用1個map去完成這個任務,肯定是比較耗時的,這種情況下,我們要考慮將這一個文件合理的拆分成多個,
這樣就可以用多個map任務去完成。
set mapred.reduce.tasks=10;
create table a_1 as
select * from a
distribute by rand(123);

這樣會將a表的記錄,隨機的分散到包含10個文件的a_1表中,再用a_1代替上面sql中的a表,則會用10個map任務去完成。
每個map任務處理大於12M(幾百萬記錄)的數據,效率肯定會好很多。

看上去,貌似這兩種有些矛盾,一個是要合並小文件,一個是要把大文件拆成小文件,這點正是重點需要關註的地方,
根據實際情況,控制map數量需要遵循兩個原則:使大數據量利用合適的map數;使單個map任務處理合適的數據量;

二、 控制hive任務的reduce數:

1. Hive自己如何確定reduce數:
reduce個數的設定極大影響任務執行效率,不指定reduce個數的情況下,Hive會猜測確定一個reduce個數,基於以下兩個設定:
hive.exec.reducers.bytes.per.reducer(每個reduce任務處理的數據量,默認為1000^3=1G)
hive.exec.reducers.max(每個任務最大的reduce數,默認為999)
計算reducer數的公式很簡單N=min(參數2,總輸入數據量/參數1)
即,如果reduce的輸入(map的輸出)總大小不超過1G,那麽只會有一個reduce任務;
如:select pt,count(1) from popt_tbaccountcopy_mes where pt = ‘2012-07-04′ group by pt;
/group/p_sdo_data/p_sdo_data_etl/pt/popt_tbaccountcopy_mes/pt=2012-07-04 總大小為9G多,因此這句有10個reduce

2. 調整reduce個數方法一:
調整hive.exec.reducers.bytes.per.reducer參數的值;
set hive.exec.reducers.bytes.per.reducer=500000000; (500M)
select pt,count(1) from popt_tbaccountcopy_mes where pt = ‘2012-07-04′ group by pt; 這次有20個reduce

3. 調整reduce個數方法二;
set mapred.reduce.tasks = 15;
select pt,count(1) from popt_tbaccountcopy_mes where pt = ‘2012-07-04′ group by pt;這次有15個reduce

4. reduce個數並不是越多越好;
同map一樣,啟動和初始化reduce也會消耗時間和資源;
另外,有多少個reduce,就會有多少個輸出文件,如果生成了很多個小文件,那麽如果這些小文件作為下一個任務的輸入,則也會出現小文件過多的問題;

5. 什麽情況下只有一個reduce;
很多時候你會發現任務中不管數據量多大,不管你有沒有設置調整reduce個數的參數,任務中一直都只有一個reduce任務;
其實只有一個reduce任務的情況,除了數據量小於hive.exec.reducers.bytes.per.reducer參數值的情況外,還有以下原因:
a) 沒有group by的匯總,比如把select pt,count(1) from popt_tbaccountcopy_mes where pt = ‘2012-07-04′ group by pt; 寫成 select count(1) from popt_tbaccountcopy_mes where pt = ‘2012-07-04′;
這點非常常見,希望大家盡量改寫。
b) 用了Order by
c) 有笛卡爾積
通常這些情況下,除了找辦法來變通和避免,我暫時沒有什麽好的辦法,因為這些操作都是全局的,所以hadoop不得不用一個reduce去完成;

同樣的,在設置reduce個數的時候也需要考慮這兩個原則:使大數據量利用合適的reduce數;使單個reduce任務處理合適的數據量;

待研究:

map的數量通常是由hadoop集群的DFS塊大小確定的,也就是輸入文件的總塊數,正常的map數量的並行規模大致是每一個Node是 10~100個,對於CPU消耗較小的作業可以設置Map數量為300個左右,但是由於hadoop的沒一個任務在初始化時需要一定的時間,因此比較合理 的情況是每個map執行的時間至少超過1分鐘。具體的數據分片是這樣的,InputFormat在默認情況下會根據hadoop集群的DFS塊大小進行分 片,每一個分片會由一個map任務來進行處理,當然用戶還是可以通過參數mapred.min.split.size參數在作業提交客戶端進行自定義設 置。還有一個重要參數就是mapred.map.tasks,這個參數設置的map數量僅僅是一個提示,只有當InputFormat 決定了map任務的個數比mapred.map.tasks值小時才起作用。同樣,Map任務的個數也能通過使用JobConf 的conf.setNumMapTasks(int num)方法來手動地設置。這個方法能夠用來增加map任務的個數,但是不能設定任務的個數小於Hadoop系統通過分割輸入數據得到的值。當然為了提高 集群的並發效率,可以設置一個默認的map數量,當用戶的map數量較小或者比本身自動分割的值還小時可以使用一個相對交大的默認值,從而提高整體 hadoop集群的效率。

如果覺得本博客對您有幫助,請 贊助作者 。

轉載請註明:lxw的大數據田地 » hive優化之——控制hive任務中的map數和reduce數

hive優化之——控制hive任務中的map數和reduce數