天天看點

Hive 設定map 和 reduce 的個數

一、    控制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任務處理合适的資料量;

繼續閱讀