大資料之Hadoop(MapReduce(四))------->企業優化
6.1 MapReduce 跑的慢的原因
Mapreduce 程式效率的瓶頸在於兩點:
1)計算機效能
CPU、記憶體、磁碟健康、網路
2)I/O 操作優化
(1)資料傾斜
(2)map和reduce數設定不合理
(3)reduce等待過久
(4)小檔案過多
(5)大量的不可分塊的超大檔案
(6)spill次數過多
(7)merge次數過多等。
6.2 MapReduce優化方法
MapReduce優化方法主要從以下六個方面考慮:
6.2.1 資料輸入
(1)合併小檔案:在執行mr任務前將小檔案進行合併,大量的小檔案會產生大量的map任務,增大map任務裝載次數,而任務的裝載比較耗時,從而導致 mr 執行較慢。
(2)採用ConbinFileInputFormat來作為輸入,解決輸入端大量小檔案場景。
6.2.2 Map階段
1)減少spill次數:通過調整io.sort.mb及sort.spill.percent引數值,增大觸發spill的記憶體上限,減少spill次數,從而減少磁碟 IO。
2)減少merge次數:通過調整io.sort.factor引數,增大merge的檔案數目,減少merge的次數,從而縮短mr處理時間。
3)在 map 之後先進行combine處理,減少 I/O。
6.2.3 Reduce階段
1)合理設定map和reduce數:兩個都不能設定太少,也不能設定太多。太少,會導致task等待,延長處理時間;太多,會導致 map、reduce任務間競爭資源,造成處理超時等錯誤。
2)設定map、reduce共存:調整slowstart.completedmaps引數,使map執行到一定程度後,reduce也開始執行,減少reduce的等待時間。
3)規避使用reduce,因為Reduce在用於連線資料集的時候將會產生大量的網路消耗。
4)合理設定reduc端的buffer,預設情況下,資料達到一個閾值的時候,buffer中的資料就會寫入磁碟,然後reduce會從磁碟中獲得所有的資料。也就是說,buffer和reduce是沒有直接關聯的,中間多個一個寫磁碟->讀磁碟的過程,既然有這個弊端,那麼就可以通過引數來配置,使得buffer中的一部分資料可以直接輸送到reduce,從而減少IO開銷:mapred.job.reduce.input.buffer.percent,預設為0.0。當值大於0的時候,會保留指定比例的記憶體讀buffer中的資料直接拿給reduce使用。這樣一來,設定buffer需要記憶體,讀取資料需要記憶體,reduce計算也要記憶體,所以要根據作業的執行情況進行調整。
6.2.4 IO傳輸
1)採用資料壓縮的方式,減少網路IO的的時間。安裝Snappy和LZOP壓縮編碼器。
2)使用SequenceFile二進位制檔案
6.2.5 資料傾斜問題
1)資料傾斜現象
資料頻率傾斜——某一個區域的資料量要遠遠大於其他區域。
資料大小傾斜——部分記錄的大小遠遠大於平均值。
2)如何收集傾斜資料
在reduce方法中加入記錄map輸出鍵的詳細情況的功能。
public static final String MAX_VALUES = "skew.maxvalues"; private int maxValueThreshold; @Override public void configure(JobConf job) { maxValueThreshold = job.getInt(MAX_VALUES, 100); } @Override public void reduce(Text key, Iterator<Text> values, OutputCollector<Text, Text> output, Reporter reporter) throws IOException { int i = 0; while (values.hasNext()) { values.next(); i++; } if (++i > maxValueThreshold) { log.info("Received " + i + " values for key " + key); } } |
3)減少資料傾斜的方法
方法1:抽樣和範圍分割槽
可以通過對原始資料進行抽樣得到的結果集來預設分割槽邊界值。
方法2:自定義分割槽
另一個抽樣和範圍分割槽的替代方案是基於輸出鍵的背景知識進行自定義分割槽。例如,如果map輸出鍵的單詞來源於一本書。其中大部分必然是省略詞(stopword)。那麼就可以將自定義分割槽將這部分省略詞傳送給固定的一部分reduce例項。而將其他的都發送給剩餘的reduce例項。
方法3:Combine
使用Combine可以大量地減小資料頻率傾斜和資料大小傾斜。在可能的情況下,combine的目的就是聚合並精簡資料。
方法4:採用Map Join,儘量避免Reduce Join。
6.2.6 常用的調優引數
1)資源相關引數
(1)以下引數是在使用者自己的mr應用程式中配置就可以生效(mapred-default.xml)
配置引數 |
引數說明 |
mapreduce.map.memory.mb |
一個Map Task可使用的資源上限(單位:MB),預設為1024。如果Map Task實際使用的資源量超過該值,則會被強制殺死。 |
mapreduce.reduce.memory.mb |
一個Reduce Task可使用的資源上限(單位:MB),預設為1024。如果Reduce Task實際使用的資源量超過該值,則會被強制殺死。 |
mapreduce.map.cpu.vcores |
每個Map task可使用的最多cpu core數目,預設值: 1 |
mapreduce.reduce.cpu.vcores |
每個Reduce task可使用的最多cpu core數目,預設值: 1 |
mapreduce.reduce.shuffle.parallelcopies |
每個reduce去map中拿資料的並行數。預設值是5 |
mapreduce.reduce.shuffle.merge.percent |
buffer中的資料達到多少比例開始寫入磁碟。預設值0.66 |
mapreduce.reduce.shuffle.input.buffer.percent |
buffer大小佔reduce可用記憶體的比例。預設值0.7 |
mapreduce.reduce.input.buffer.percent |
指定多少比例的記憶體用來存放buffer中的資料,預設值是0.0 |
(2)應該在yarn啟動之前就配置在伺服器的配置檔案中才能生效(yarn-default.xml)
配置引數 |
引數說明 |
yarn.scheduler.minimum-allocation-mb 1024 |
給應用程式container分配的最小記憶體 |
yarn.scheduler.maximum-allocation-mb 8192 |
給應用程式container分配的最大記憶體 |
yarn.scheduler.minimum-allocation-vcores 1 |
每個container申請的最小CPU核數 |
yarn.scheduler.maximum-allocation-vcores 32 |
每個container申請的最大CPU核數 |
yarn.nodemanager.resource.memory-mb 8192 |
給containers分配的最大實體記憶體 |
(3)shuffle效能優化的關鍵引數,應在yarn啟動之前就配置好(mapred-default.xml)
配置引數 |
引數說明 |
mapreduce.task.io.sort.mb 100 |
shuffle的環形緩衝區大小,預設100m |
mapreduce.map.sort.spill.percent 0.8 |
環形緩衝區溢位的閾值,預設80% |
2)容錯相關引數(mapreduce效能優化)
配置引數 |
引數說明 |
mapreduce.map.maxattempts |
每個Map Task最大重試次數,一旦重試引數超過該值,則認為Map Task執行失敗,預設值:4。 |
mapreduce.reduce.maxattempts |
每個Reduce Task最大重試次數,一旦重試引數超過該值,則認為Map Task執行失敗,預設值:4。 |
mapreduce.task.timeout |
Task超時時間,經常需要設定的一個引數,該引數表達的意思為:如果一個task在一定時間內沒有任何進入,即不會讀取新的資料,也沒有輸出資料,則認為該task處於block狀態,可能是卡住了,也許永遠會卡主,為了防止因為使用者程式永遠block住不退出,則強制設定了一個該超時時間(單位毫秒),預設是600000。如果你的程式對每條輸入資料的處理時間過長(比如會訪問資料庫,通過網路拉取資料等),建議將該引數調大,該引數過小常出現的錯誤提示是“AttemptID:attempt_14267829456721_123456_m_000224_0 Timed out after 300 secsContainer killed by the ApplicationMaster.”。 |
6.3 HDFS小檔案優化方法
6.3.1 HDFS小檔案弊端
HDFS上每個檔案都要在namenode上建立一個索引,這個索引的大小約為150byte,這樣當小檔案比較多的時候,就會產生很多的索引檔案,一方面會大量佔用namenode的記憶體空間,另一方面就是索引檔案過大是的索引速度變慢。
6.3.2 解決方案
1)Hadoop Archive:
是一個高效地將小檔案放入HDFS塊中的檔案存檔工具,它能夠將多個小檔案打包成一個HAR檔案,這樣在減少namenode記憶體使用的同時。
2)Sequence file:
sequence file由一系列的二進位制key/value組成,如果key為檔名,value為檔案內容,則可以將大批小檔案合併成一個大檔案。
3)CombineFileInputFormat:
CombineFileInputFormat是一種新的inputformat,用於將多個檔案合併成一個單獨的split,另外,它會考慮資料的儲存位置。
4)開啟JVM重用
對於大量小檔案Job,可以開啟JVM重用會減少45%執行時間。
JVM重用理解:一個map執行一個jvm,重用的話,在一個map在jvm上執行完畢後,jvm繼續執行其他jvm
具體設定:mapreduce.job.jvm.numtasks值在10-20之間。