MapTask和ReduceTask工作機制合起來即爲MapReduce工作機制
一、MapTask工作機制
-
Read
階段:MapTask
通過用戶編寫的RecordReader
,從輸入InputSplit
中解析出一個個key/value
; -
Map
階段:該節點主要是將解析出的key/value
交給用戶編寫map()
函數處理,併產生一系列新的key/value
; -
Collect
收集階段:在用戶編寫map()
函數中,當數據處理完成後,一般會調用OutputCollector.collect()
輸出結果。在該函數內部,它會將生成的key/value
分區(調用Partitioner
),並寫入一個環形內存緩衝區中; -
Spill
階段:即“溢寫”,當環形緩衝區滿後,MapReduce
會將數據寫到本地磁盤上,生成一個臨時文件。需要注意的是,將數據寫入本地磁盤之前,先要對數據進行一次本地排序,並在必要時對數據進行合併、壓縮等操作;(1)利用快速排序算法對緩存區內的數據進行排序,排序方式是,先按照分區編號
Partition
進行排序,然後按照key
進行排序。這樣,經過排序後,數據以分區爲單位聚集在一起,且同一分區內所有數據按照key
有序;(2)按照分區編號由小到大依次將每個分區中的數據寫入任務工作目錄下的臨時文件
output/spillN.out
(N表示當前溢寫次數)中。如果用戶設置了Combiner
,則寫入文件之前,對每個分區中的數據進行一次聚集操作;(3)將分區數據的元信息寫到內存索引數據結構
SpillRecord
中,其中每個分區的元信息包括在臨時文件中的偏移量、壓縮前數據大小和壓縮後數據大小。如果當前內存索引大小超過1MB,則將內存索引寫到文件output/spillN.out.index
中。 -
Combine
階段:當所有數據處理完成後,MapTask
對所有臨時文件進行一次合併,以確保最終只會生成一個數據文件。 -
當所有數據處理完後,
MapTask
會將所有臨時文件合併成一個大文件,並保存到文件output/file.out
中,同時生成相應的索引文件output/file.out.index
。在進行文件合併過程中,
MapTask
以分區爲單位進行合併。對於某個分區,它將採用多輪遞歸合併的方式。每輪合併io.sort.factor
(默認10)個文件,並將產生的文件重新加入待合併列表中,對文件排序後,重複以上過程,直到最終得到一個大文件。讓每個MapTask
最終只生成一個數據文件,可避免同時打開大量文件和同時讀取大量小文件產生的隨機讀取帶來的開銷。
二、ReduceTask工作機制
-
ReduceTask
工作機制(1)
Copy
階段:ReduceTask
從各個MapTask
上遠程拷貝一片數據,並針對某一片數據,如果其大小超過一定閾值,則寫到磁盤上,否則直接放到內存中;(2)
Merge
階段:在遠程拷貝數據的同時,ReduceTask
啓動了兩個後臺線程對內存和磁盤上的文件進行合併,以防止內存使用過多或磁盤上文件過多;(3)Sort階段:按照
MapReduce
語義,用戶編寫reduce()
函數輸入數據是按key
進行聚集的一組數據。爲了將key
相同的數據聚在一起,Hadoop
採用了基於排序的策略。由於各個MapTask
已經實現對自己的處理結果進行了局部排序,因此,ReduceTask
只需對所有數據進行一次歸併排序即可;(4)
Reduce
階段:reduce()
函數將計算結果寫到HDFS
上。 -
設置
ReduceTask
並行度(個數)ReduceTask
的並行度同樣影響整個Job
的執行併發度和執行效率,但與MapTask
的併發數由切片數決定不同,ReduceTask
數量的決定是可以直接手動設置:// 默認值是1,手動設置爲4 job.setNumReduceTasks(4);
-
注意事項:
(1)
ReduceTask=0
,表示沒有Reduce
階段,輸出文件個數和Map
個數一致。(2)
ReduceTask
默認值是1,所以輸出文件個數爲1。(3)如果數據分佈不均勻,就有可能在
Reduce
階段產生數據傾斜。(4)
ReduceTask
數量並不是任意設置,還要考慮業務邏輯需求,有些情況下,需要計算全局彙總結果,就只有一個ReduceTask
。(5)具體有多少個
ReduceTask
,需要根據集羣性能而定。(6)如果分區數不是1,但
ReduceTask
爲1,則不執行分區過程,因爲在MapTask
源碼中,執行分區的前提是先判斷ReduceNum
個數是否大於1,不大於1肯定不執行。