hive優化策略之控制map數和reduce數

我們可以通過控制map和reduce的數量來平衡資源達到優化程序的目的。


一、map數量


map的數量與你輸入的文件個數和大小有關,你可以通過set dfs.block.size;來查看集羣設置的文件塊大小,該參數不能自定義修改。


hive> set dfs.block.size;
dfs.block.size=134217728


例如你輸入一個文件file1,大小是450M,那麼hadoop會把它劃分成4個塊,3個128M和1個66M的文件塊,從而map的數量就是4。
例如你輸入四個文件file1、file2、file3、file4,大小分別是10M、20M、100M、140M,那麼hadoop就會將其分成5個文件塊,分別是10、20、100、128、2,從而map的數量就是5。


注意:map也不是說越多越好,如果輸入的文件都是小文件,每個文件都會被當做一個文件塊來進行處理,每一個文件就是一個map,因爲map任務啓動和初始化的時候會大於邏輯處理的時間,那麼會造成資源的浪費,而且可同時執行的map數是受限制的

一個tasktracker能夠同時運行最多多少個map和reduce任務
一個tasktracker能夠同時運行最多多少個map任務:由mapred.tasktracker.map.tasks.maximum屬性控制,默認值是2個任務。相應的,一個tasktracker能夠同時運行的最多reduce任務數由mapred.tasktracker.reduce.task.maximum屬性控制,默認也是2。詳見《Hadoop權威指南》第二版P269頁。
在一個tasktracker上能夠同時運行的任務數取決於一臺機器有多少個處理器。由於MapReduce作業通常是I/O-bound,因此將任務數設定爲超出處理器數也有一定道理,能夠獲得更好的利用率。
至於到底需要運行多少個任務,則依賴於相關作業的CPU使用情況。
但經驗法則是任務數(包括map和reduce任務)與處理器數的比值最好在1和2之間。


而且每個map需要處理文件大小也不是越接近128M就越好,同時你還需要根據這個文件記錄的數量來進行評估,比如說文件中只有一列,那麼大概得有幾千萬行,用一個map來處理是比較費時的,看上去,貌似這兩種有些矛盾,一個是要合併小文件,一個是要把大文件拆成小文件,這點正是重點需要關注的地方,根據實際情況,控制map數量需要遵循兩個原則:使大數據量利用合適的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(幾百萬記錄)的數據,效率肯定會好很多。




二、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


我們可以通過調整默認每個reduce處理數據的大小來進達到調整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


也可以通過set mapred.reduce.tasks = 15;直接指定reduce的數量


同樣reduce的數量也不是越多越好,因爲啓動reduce也會消耗時間和資源,另外有多少個reduce就會輸出多少個文件,如果生成了多個小文件,那麼這些小文件作爲下一個任務的輸入也會出現小文件過多的問題。


有3種情況下,不管你怎麼設置調整reduce的個數,任務中也就一直只有一個reduce的任務。
1.沒有使用group by的情況下使用select * from tablename;
2.用order by
3.使用笛卡爾積


因爲這些操作都是全局的,所以hadoop不得不使用一個reduce來進行處理,同樣設置reduce也需要考慮兩個原則,使用大數據量利用合適的reduce數,使用單個reduce處理合適的數據量。









發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章