hadoop JOB的效能優化實踐

weixin_34377065發表於2014-07-24

使用了幾個月的hadoopMR,對遇到過的效能問題做點筆記,這裡只涉及job的效能優化,沒有接觸到

hadoop叢集,作業系統,任務排程策略這些方面的問題。

hadoop MR在做大資料量分析時候有限的計算資源情況下只能不斷的優化程式。

優化可以從兩個方面進行:

1.hadoop配置

2.程式程式碼

程式程式碼包括的方面很多:job設計,演算法,資料結構,程式碼編寫。

hadoop配置優化

hadoop配置可分為mapp配置,reducer配置和hdfs配置。關於hadoop mapper和reducer階段

處理流程和引數意義可以看這個帖子,說的比較詳細hadoop mr 引數意義

這裡再補充幾個配置:

dfs.block.size

這個配置項定義了在HDFS上每個block的大小,它的值是以位元組為單位。

可以在配置檔案hadoop-site.xml(Hadoop 0.20 以前版本)定義,

也可以在JobConf裡定義。hdfs中block size定義是以檔案為粒度的。

 hadoop的mapper數基本由輸入檔案的block數決定,如果輸入的block

size不夠大,導致mapper處理時間很短(不到一分鐘),大量這樣的mapper

會嚴重降低計算效能。但是如果輸入檔案都是小檔案,就算blocksize再大,每個

檔案也會佔一個block,這時候要通過合併小檔案來減少mapper數,設定blocksize

是沒用的。命令列設定塊大小可以加引數,0.20以後的用

hadoop fs -D dfs.block.size=134217728 -put local_name remote_location

之前的可以用fs.local.block.size 引數

 

除了blocksize hadoop的inputformat也提供了在block的基礎上更細粒度控制mapper

輸入塊大小,比如當前輸入塊128M,設定了最大分割size為64,則原先一個塊被切分

成兩個spliter了,也就產生了兩個mapper。用這種方法可以有效增加mapper數,但對減少

mapper數好像沒用。

FileInputFormat.setMaxInputSplitSize(job, size)

FileInputFormat.setMinInputSplitSize(job, size)

 

mapred.min.split.size這個引數也可以起到同樣效果

 

mapred.map.tasks.speculative.execution 和 

mapred.reduce.tasks.speculative.execution

這兩個選項是設定推測執行的任務,當所有task都開始執行之後,Job Tracker會統計所有任務的平均進度,

如果某個task所在的task node機器配置比較低或者CPU load很高(原因很多),導致任務執行比總體任務的平均執行要慢,

此時Job Tracker會啟動一個新的任務(duplicate task),這個新任務就是推測任務,原有任務和新任務哪個先執行完就把另外一個kill掉,

這也是我們經常在Job Tracker頁面看到任務執行成功,但是總有些任務被kill,就是這個原因。推測任務也是要佔用計算資源,

因此計算資源緊張,任務執行本身很耗資源情況下可以考慮設定成false,禁止執行。

 

io.sort.mb

 

以MB為單位,預設100M,通常來看,這個值太小了,這個選項定義了map輸出結果在記憶體佔用buffer的大小,當buffer達到一定閾值,

會啟動一個後臺執行緒來對buffer的內容進行排序,然後寫入本地磁碟(一個spill檔案)。可以觀察hadoop的日誌,如果spill次數比較多說明

這個快取大小設定太低,特別是那種mapper中處理資料會增多的邏輯尤其可以關注下。

 

根據map輸出資料量的大小,可以適當的調整buffer的大小,注意是適當的調整,不是越大越好,假設記憶體無限大,io.sort.mb=1024(1G),

和io.sort.mb=300 (300M),前者未必比後者快,因為1G的資料排序一次和排序3次,每次300MB,一定是後者快(分而治之的思想)。

 

io.sort.spill.percent

 

這個值就是上述buffer的閾值,預設是0.8,既80%,當buffer中的資料達到這個閾值,後臺執行緒會起來對buffer中已有的資料進行排序,

然後寫入磁碟,此時map輸出的資料繼續往剩餘的20% buffer寫資料,如果buffer的剩餘20%寫滿,排序還沒結束,map task被block等待。

如果你確認map輸出的資料基本有序(很少見),排序時間很短,可以將這個閾值適當調高,更理想的,如果你的map輸出是有序的資料(基本不可能吧?),

那麼可以把buffer設的更大,閾值設定為1.

 

Io.sort.factor

 

同時開啟磁碟spill進行並行合併的檔案數,預設是10。

當一個map task執行完之後,本地磁碟上(mapred.local.dir)有若干個spill檔案,map task最後做的一件事就是執行merge sort,

把這些spill檔案合成一個檔案(partition),有時候我們會自定義partition函式,就是在這個時候被呼叫的。

執行merge sort的時候,每次同時開啟多少個spill檔案,就是由io.sort.factor決定的。開啟的檔案越多,不一定merge sort就越快,所以也要根據資料情況適當的調整。

補充:merge排序的結果是兩個檔案,一個是index,另一個是資料檔案,index檔案記錄了每個不同的key在資料檔案中的偏移量(這就是partition)

 

程式碼優化

有空再寫

 

 

 

 

 

 

 

各種配置

Mapper端配置

 

 

 

 

 

1.Map邏輯處理後資料被展開,寫磁碟次數劇增,可以觀察日誌中的spill次數,調整各個引數

 

2.中間結果能不展開就不展開,儘量縮小Mapper和reducer之間的資料傳遞

 

3.distribute cache中載入的資料能不用hashmap就儘量不要用,hashmap會使得記憶體佔用量是原資料的5-10倍,其中

引用佔了大量空間

 

4.distribute cache中載入的資料要儘可能簡單,如果有複雜的處理邏輯可以單獨開闢Mapper Reducer進行一輪處理,

避免每次mapper都要處理一遍,儘可能減少distribute cache的資料量

 

5.觀察GC的情況,有時候是因為記憶體佔用量高,頻繁GC,嚴重影響處理速度

 

6.當邏輯本身很簡單,但是處理速度很慢時候首先要懷疑Mapper和Reducer之間傳輸資料量過大,其次是GC情況

 

7.適當控制mapper的數量,特別是有distribute cache的場景

相關文章