MapReduce是幹啥的

1. MapReduce是幹啥的

由於沒找到谷歌的示意圖,因此我想借用一張Hadoop項目的結構圖來講明下MapReduce所處的位置,以下圖。java

Hadoop實際上就是谷歌三寶的開源實現,Hadoop MapReduce對應Google MapReduce,HBase對應BigTable,HDFS對應GFS。HDFS(或GFS)爲上層提供高效的非結構化存儲服務,HBase(或BigTable)是提供結構化數據服務的分佈式數據庫,Hadoop MapReduce(或Google MapReduce)是一種並行計算的編程模型,用於做業調度。c++

GFS和BigTable已經爲咱們提供了高性能、高併發的服務,可是並行編程可不是全部程序員都玩得轉的活兒,若是咱們的應用自己不能併發,那GFS、BigTable也都是沒有意義的。MapReduce的偉大之處就在於讓不熟悉並行編程的程序員也能充分發揮分佈式系統的威力。程序員

簡單歸納的說,MapReduce是將一個大做業拆分爲多個小做業的框架(大做業和小做業應該本質是同樣的,只是規模不一樣),用戶須要作的就是決定拆成多少份,以及定義做業自己。數據庫

下面用一個貫穿全文的例子來解釋MapReduce是如何工做的。編程

2. 例子:統計詞頻

若是我想統計下過去10年計算機論文出現最多的幾個單詞,看看你們都在研究些什麼,那我收集好論文後,該怎麼辦呢?小程序

方法一:我能夠寫一個小程序,把全部論文按順序遍歷一遍,統計每個遇到的單詞的出現次數,最後就能夠知道哪幾個單詞最熱門了。緩存

這種方法在數據集比較小時,是很是有效的,並且實現最簡單,用來解決這個問題很合適。多線程

方法二:寫一個多線程程序,併發遍歷論文。併發

這個問題理論上是能夠高度併發的,由於統計一個文件時不會影響統計另外一個文件。當咱們的機器是多核或者多處理器,方法二確定比方法一高效。可是寫一個多線程程序要比方法一困難多了,咱們必須本身同步共享數據,好比要防止兩個線程重複統計文件。app

方法三:把做業交給多個計算機去完成。

咱們可使用方法一的程序,部署到N臺機器上去,而後把論文集分紅N份,一臺機器跑一個做業。這個方法跑得足夠快,可是部署起來很麻煩,咱們要人工把程序copy到別的機器,要人工把論文集分開,最痛苦的是還要把N個運行結果進行整合(固然咱們也能夠再寫一個程序)。

方法四:讓MapReduce來幫幫咱們吧!

MapReduce本質上就是方法三,可是如何拆分文件集,如何copy程序,如何整合結果這些都是框架定義好的。咱們只要定義好這個任務(用戶程序),其它都交給MapReduce。

在介紹MapReduce如何工做以前,先講講兩個核心函數map和reduce以及MapReduce的僞代碼。

3. map函數和reduce函數

map函數和reduce函數是交給用戶實現的,這兩個函數定義了任務自己。

 

  • map函數:接受一個鍵值對(key-value pair),產生一組中間鍵值對。MapReduce框架會將map函數產生的中間鍵值對裏鍵相同的值傳遞給一個reduce函數。
  • reduce函數:接受一個鍵,以及相關的一組值,將這組值進行合併產生一組規模更小的值(一般只有一個或零個值)。

統計詞頻的MapReduce函數的核心代碼很是簡短,主要就是實現這兩個函數。

 

[plain] view plain copy

 print?

  1. map(String key, String value):  
  2.     // key: document name  
  3.     // value: document contents  
  4.     for each word w in value:  
  5.         EmitIntermediate(w, "1");  
  6.   
  7. reduce(String key, Iterator values):  
  8.     // key: a word  
  9.     // values: a list of counts  
  10.     int result = 0;  
  11.     for each v in values:  
  12.         result += ParseInt(v);  
  13.         Emit(AsString(result));  

 

在統計詞頻的例子裏,map函數接受的鍵是文件名,值是文件的內容,map逐個遍歷單詞,每遇到一個單詞w,就產生一箇中間鍵值對<w, "1">,這表示單詞w咱又找到了一個;MapReduce將鍵相同(都是單詞w)的鍵值對傳給reduce函數,這樣reduce函數接受的鍵就是單詞w,值是一串"1"(最基本的實現是這樣,但能夠優化),個數等於鍵爲w的鍵值對的個數,而後將這些「1」累加就獲得單詞w的出現次數。最後這些單詞的出現次數會被寫到用戶定義的位置,存儲在底層的分佈式存儲系統(GFS或HDFS)。

4. MapReduce是如何工做的

 

上圖是論文裏給出的流程圖。一切都是從最上方的user program開始的,user program連接了MapReduce庫,實現了最基本的Map函數和Reduce函數。圖中執行的順序都用數字標記了。

  1. MapReduce庫先把user program的輸入文件劃分爲M份(M爲用戶定義),每一份一般有16MB到64MB,如圖左方所示分紅了split0~4;而後使用fork將用戶進程拷貝到集羣內其它機器上。
  2. user program的副本中有一個稱爲master,其他稱爲worker,master是負責調度的,爲空閒worker分配做業(Map做業或者Reduce做業),worker的數量也是能夠由用戶指定的。
  3. 被分配了Map做業的worker,開始讀取對應分片的輸入數據,Map做業數量是由M決定的,和split一一對應;Map做業從輸入數據中抽取出鍵值對,每個鍵值對都做爲參數傳遞給map函數,map函數產生的中間鍵值對被緩存在內存中。
  4. 緩存的中間鍵值對會被按期寫入本地磁盤,並且被分爲R個區,R的大小是由用戶定義的,未來每一個區會對應一個Reduce做業;這些中間鍵值對的位置會被通報給master,master負責將信息轉發給Reduce worker。
  5. master通知分配了Reduce做業的worker它負責的分區在什麼位置(確定不止一個地方,每一個Map做業產生的中間鍵值對均可能映射到全部R個不一樣分區),當Reduce worker把全部它負責的中間鍵值對都讀過來後,先對它們進行排序,使得相同鍵的鍵值對彙集在一塊兒。由於不一樣的鍵可能會映射到同一個分區也就是同一個Reduce做業(誰讓分區少呢),因此排序是必須的。
  6. reduce worker遍歷排序後的中間鍵值對,對於每一個惟一的鍵,都將鍵與關聯的值傳遞給reduce函數,reduce函數產生的輸出會添加到這個分區的輸出文件中。
  7. 當全部的Map和Reduce做業都完成了,master喚醒正版的user program,MapReduce函數調用返回user program的代碼。

全部執行完畢後,MapReduce輸出放在了R個分區的輸出文件中(分別對應一個Reduce做業)。用戶一般並不須要合併這R個文件,而是將其做爲輸入交給另外一個MapReduce程序處理。整個過程當中,輸入數據是來自底層分佈式文件系統(GFS)的,中間數據是放在本地文件系統的,最終輸出數據是寫入底層分佈式文件系統(GFS)的。並且咱們要注意Map/Reduce做業和map/reduce函數的區別:Map做業處理一個輸入數據的分片,可能須要調用屢次map函數來處理每一個輸入鍵值對;Reduce做業處理一個分區的中間鍵值對,期間要對每一個不一樣的鍵調用一次reduce函數,Reduce做業最終也對應一個輸出文件。

我更喜歡把流程分爲三個階段。第一階段是準備階段,包括一、2,主角是MapReduce庫,完成拆分做業和拷貝用戶程序等任務;第二階段是運行階段,包括三、四、五、6,主角是用戶定義的map和reduce函數,每一個小做業都獨立運行着;第三階段是掃尾階段,這時做業已經完成,做業結果被放在輸出文件裏,就看用戶想怎麼處理這些輸出了。

5. 詞頻是怎麼統計出來的

結合第四節,咱們就能夠知道第三節的代碼是如何工做的了。假設我們定義M=5,R=3,而且有6臺機器,一臺master。

這幅圖描述了MapReduce如何處理詞頻統計。因爲map worker數量不夠,首先處理了分片一、三、4,併產生中間鍵值對;當全部中間值都準備好了,Reduce做業就開始讀取對應分區,並輸出統計結果。

6. 用戶的權利

用戶最主要的任務是實現map和reduce接口,但還有一些有用的接口是向用戶開放的。

  • an input reader。這個函數會將輸入分爲M個部分,而且定義瞭如何從數據中抽取最初的鍵值對,好比詞頻的例子中定義文件名和文件內容是鍵值對。
  • a partition function。這個函數用於將map函數產生的中間鍵值對映射到一個分區裏去,最簡單的實現就是將鍵求哈希再對R取模。
  • a compare function。這個函數用於Reduce做業排序,這個函數定義了鍵的大小關係。
  • an output writer。負責將結果寫入底層分佈式文件系統。
  • a combiner function。實際就是reduce函數,這是用於前面提到的優化的,好比統計詞頻時,若是每一個<w, "1">要讀一次,由於reduce和map一般不在一臺機器,很是浪費時間,因此能夠在map執行的地方先運行一次combiner,這樣reduce只須要讀一次<w, "n">了。
  • map和reduce函數就很少說了。

7. MapReduce的實現

目前MapReduce已經有多種實現,除了谷歌本身的實現外,還有著名的hadoop,區別是谷歌是c++,而hadoop是用java。另外斯坦福大學實現了一個在多核/多處理器、共享內存環境內運行的MapReduce,稱爲Phoenix(介紹),相關的論文發表在07年的HPCA,是當年的最佳論文哦!

相關文章
相關標籤/搜索