Hadoop越來越火,圍繞Hadoop的子項目更是增長迅速,光Apache官網上列出來的就十幾個,但是萬變不離其宗,大部分項目都是基於Hadoop Common。
MapReduce更是核心中的核心。那麼到底什麼是MapReduce,它具體是怎麼工作的呢?
關於它的原理,說簡單也簡單,隨便畫個圖噴一下Map和Reduce兩個階段似乎就完了。但其實這裏面還包含了很多的子階段,尤其是Shuffle,很多資料裏都把它稱爲MapReduce的“心臟”,和所謂“奇蹟發生的地方”。真正能說清楚其中關係的人就沒那麼多了。可是瞭解這些流程對我們理解和掌握MapReduce並對其進行調優是非常有用的。
首先我們看一幅圖,包含了從頭到尾的整個過程,後面對所有步驟的解釋都以此圖作爲參考(此圖100%原創)
這張圖簡單來說,就是說在我們常見的Map和Reduce之間還有一系列的過程,其中包括partition、Sort、Combine、copy、merge等,而這些過程往往被統稱爲“Shuffle”也就是“混洗”,而Shuffle的目的就是對數據進行梳理,排序,以便科學的方式分發給每個Reducer,以便高效的進行計算和處理(難怪人家說這事奇蹟發生的地方,原理這裏面有這麼多花花,能沒奇蹟嘛?)
如果您是Hadoop的大牛,看了這幅圖可能馬上就要跳出來了,不對!還有一個spill過程云云。。。
且慢,關於spill,我認爲只是一個實現細節,其實就是MapReduce利用內存緩衝的方式提高效率,整個的過程和原理並沒有受影響,所以在此忽略掉spill過程,以便更好了解。
光看原理圖還是有點費解是吧?沒錯!我一直認爲,沒有例子的文章就是耍流氓,所以我們就用大家耳熟能詳的WordCount作爲例子,開始我們的討論。
先創建兩個文本文件,作爲我們例子的輸入:
file1 內容爲:
- My name is Tony
- My company is pivotal
- My name is Lisa
- My company is EMC
第一步:對輸入分片進行map()處理
首先我們的輸入就是兩個文件,默認情況下是兩個split,對應前面圖中的split0,split1。兩個split默認會分給兩個Mapper來處理,WordCount例子相當地暴力,這一步裏面就是把文件內容分解爲單詞和1,其中的單詞就是我們的key,後面的數字就是對應的值,也就是value【在此假設各位都對WordCount程序爛熟於心】。
那麼對應兩個Mapper的輸出就是:
split0被處理後的數據爲:
- My 1
- name 1
- is 1
- Tony 1
- My 1
- company 1
- is 1
- Pivotal 1
- My 1
- name 1
- is 1
- Lisa 1
- My 1
- company 1
- is 1
- EMC 1
partition是什麼?partition就是分區。
爲什麼要分區?因爲有時候會有多個Reducer,partition就是提前對輸入進行處理,根據將來的Reducer進行分區,到時候Reducer處理的時候,只需要處理分給自己的數據就可以了。
如何分區?主要的分區方法就是按照key不同,把數據分開,其中很重要的一點就是要保證key的唯一性,因爲將來做Reduce的時候很可能是在不同的節點上做的,如果一個key同時存在兩個節點上,Reduce的結果就會出問題,所以很常見的partition方法就是哈希。
結合我們的例子,我們這裏假設有兩個Reducer,前面兩個Split做完Partition的結果就會如下:
split0的數據經過map()後再進行分區
partition 1:
- company 1
- is 1
- is 1
- My 1
- My 1
- name 1
- Pivotal 1
- Tony 1
split1的數據經過map()後再進行分區(同split0):
partition 1:
- company 1
- is 1
- is 1
- EMC 1
Partition 2:
- My 1
- My 1
- name 1
- Lisa 1
第三步:sort
sort就是排序咯,其實這個過程在我看來並不是必須的,完全可以交給客戶端自己的程序來處理。那爲什麼還要排序呢?可能是寫MapReduce的大牛們想,“大部分reduce程序應該都希望輸入的是已經按key排序號的數據,如果是這樣,那麼我們就乾脆順手幫你做掉啦!”
那麼我們假設對前面的數據再進行排序,結果如下:
split0 的partition 1中的數據排序後如下:
- company 1
- is 1
- is 1
split0的partition 2中的數據排序後如下:
- My 1
- My 1
- name 1
- Pivotal 1
- Tony 1
split1的partition 1中的數據排序後如下:
- company 1
- EMC 1
- is 1
- is 1
split1的partition 2中的數據排序後如下:
- Lisa 1
- My 1
- My 1
- name 1
第四步:Combine
什麼是Combine呢?combine其實可以理解爲一個mini Reduce過程,它發生在前面Map的輸出結果之後,目的就是在結果送到Reducer之前先對其進行一次計算,以減少文件的大小,方便後面的傳輸。但這一步不是必須的。
按照前面的輸出,執行Combine:
split0的partition 1的數據爲:
- company 1
- is 2
- My 2
- name 1
- Pivotal 1
- Tony 1
split1的partition1 的數據爲:
- Partition 1:
- company 1
- EMC 1
- is 2
split1的partition 2的數據爲:
- Lisa 1
- My 2
- name 1
第五步:copy
下面就要準備把輸出結果傳送給Reducer了。這個階段被稱爲Copy,但事實上我認爲叫它Download更爲合適,因爲實現的時候,是通過http的方式,由Reducer節點向各個Mapper節點下載屬於自己分區的數據。
那麼根據前面的partition,下載完的結果如下:
Reducer 節點1的共包含兩個文件(split0的partition1和split1的partition1):
- Partition 1:
- company 1
- is 2
- Partition 1:
- company 1
- EMC 1
- is 2
Reducer 節點2也是兩個文件(split0的partition1和split1的partition2):
- Partition 2:
- My 2
- name 1
- Pivotal 1
- Tony 1
- Partition 2:
- Lisa 1
- My 2
- name 1
第六步:Merge
如上一步所示,此時Reducer得到的文件是從不同的Mapper那裏下載到的,需要對他們進行合併爲一個文件,所以下面這一步就是Merge,結果如下:
Reducer 節點1的數據如下:
- company 1
- company 1
- EMC 1
- is 2
- is 2
- Lisa 1
- My 2
- My 2
- name 1
- name 1
- Pivotal 1
- Tony 1
第七步:reduce處理
終於可以進行最後的Reduce啦,這步相當簡單咯,根據每個文件中的內容最後做一次統計,結果如下:
Reducer節點1的數據:
- company 2
- EMC 1
- is 4
- Lisa 1
- My 4
- name 2
- Pivotal 1
- Tony 1
如果你在你自己的環境中運行的WordCount只有part-r-00000一個文件的話,那應該是因爲你使用的是默認設置,默認一個Job只有一個Reducer。
如果你想設置兩個,你可以:
1.在源代碼中加入 job.setNumReduceTasks(2),設置這個Job的Reducer爲兩個。
或者
2.在mapred-site.xml中設置下面參數並重啓服務
- <property>
- <name>mapred.reduce.tasks</name>
- <value>2</value>
- </property>
結束語:
本文大致描述了一下MapReduce的整個過程以及每個階段所做的事情,並沒有涉及具體的Job,resource的管理和控制,因爲那個是第一代MapReduce框架和Yarn框架的主要區別。而兩代框架中上述MapReduce的原理是差不多的。