標籤:blog http io ar 使用 java for strong sp
轉自:http://blog.csdn.net/opennaive/article/details/7514146
1. MapReduce是幹啥的因為沒找到Google的,所以我想借用一張Hadoop項目的結構圖來說明下MapReduce所處的位置,如。Hadoop實際上就是Google三寶的開源實現,Hadoop MapReduce對應Google MapReduce,HBase對應BigTable,HDFS對應GFS。HDFS(或GFS)為上層提供高效的非結構化儲存服務,HBase(或BigTable)是提供結構化資料服務的分散式資料庫,Hadoop MapReduce(或Google MapReduce)是一種並行計算的編程模型,用於作業調度。
GFS和BigTable已經為我們提供了高效能、高並發的服務,但是並行編程可不是所有程式員都玩得轉的活兒,如果我們的應用本身不能並發,那GFS、BigTable也都是沒有意義的。MapReduce的偉大之處就在於讓不熟悉並行編程的程式員也能充分發揮分布式系統的威力。
簡單概括的說,MapReduce是將一個大作業拆分為多個小作業的架構(大作業和小作業應該本質是一樣的,只是規模不同),使用者需要做的就是決定拆成多少份,以及定義作業本身。
下面用一個貫穿全文的例子來解釋MapReduce是如何工作的。
2. 例子:統計詞頻
如果我想統計下過去10年電腦論文出現最多的幾個單詞,看看大家都在研究些什麼,那我收集好論文後,該怎麼辦呢?
方法一:我可以寫一個小程式,把所有論文按順序遍曆一遍,統計每一個遇到的單詞的出現次數,最後就可以知道哪幾個單詞最熱門了。
這種方法在資料集比較小時,是非常有效,而且實現最簡單,用來解決這個問題很合適。
方法二:寫一個多線程程式,並發遍曆論文。
這個問題理論上是可以高度並發的,因為統計一個檔案時不會影響統計另一個檔案。當我們的機器是多核或者多處理器,方法二肯定比方法一高效。但是寫一個多線程程式要比方法一困難多了,我們必須自己同步共用資料,比如要防止兩個線程重複統計檔案。
方法三:把作業交給多個電腦去完成。
我們可以使用方法一的程式,部署到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 plaincopyprint?
- map(String key, String value):
- // key: document name
- // value: document contents
- for each word w in value:
- EmitIntermediate(w, "1");
-
- reduce(String key, Iterator values):
- // key: a word
- // values: a list of counts
- int result = 0;
- for each v in values:
- result += ParseInt(v);
- 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函數。圖中執行的順序都用數字標記了。
- MapReduce庫先把user program的輸入檔案劃分為M份(M為使用者定義),每一份通常有16MB到64MB,左方所示分成了split0~4;然後使用fork將使用者進程拷貝到叢集內其它機器上。
- user program的副本中有一個稱為master,其餘稱為worker,master是負責調度的,為空白閑worker分配作業(Map作業或者Reduce作業),worker的數量也是可以由使用者指定的。
- 被分配了Map作業的worker,開始讀取對應分區的輸入資料,Map作業數量是由M決定的,和split一一對應;Map作業從輸入資料中抽取出索引值對,每一個索引值對都作為參數傳遞給map函數,map函數產生的中間索引值對被緩衝在記憶體中。
- 緩衝的中間索引值對會被定期寫入本地磁碟,而且被分為R個區,R的大小是由使用者定義的,將來每個區會對應一個Reduce作業;這些中間索引值對的位置會被通報給master,master負責將資訊轉寄給Reduce worker。
- master通知分配了Reduce作業的worker它負責的分區在什麼位置(肯定不止一個地方,每個Map作業產生的中間索引值對都可能映射到所有R個不同分區),當Reduce worker把所有它負責的中間索引值對都讀過來後,先對它們進行排序,使得相同鍵的索引值對聚集在一起。因為不同的鍵可能會映射到同一個分區也就是同一個Reduce作業(誰讓分區少呢),所以排序是必須的。
- reduce worker遍曆排序後的中間索引值對,對於每個唯一的鍵,都將鍵與關聯的值傳遞給reduce函數,reduce函數產生的輸出會添加到這個分區的輸出檔案中。
- 當所有的Map和Reduce作業都完成了,master喚醒正版的user program,MapReduce函數調用返回user program的代碼。
所有執行完畢後,MapReduce輸出放在了R個分區的輸出檔案中(分別對應一個Reduce作業)。使用者通常並不需要合并這R個檔案,而是將其作為輸入交給另一個MapReduce程式處理。整個過程中,輸入資料是來自底層Distributed File System(GFS)的,中間資料是放在本地檔案系統的,最終輸出資料是寫入底層Distributed File System(GFS)的。而且我們要注意Map/Reduce作業和map/reduce函數的區別:Map作業處理一個輸入資料的分區,可能需要調用多次map函數來處理每個輸入索引值對;Reduce作業處理一個分區的中間索引值對,期間要對每個不同的鍵調用一次reduce函數,Reduce作業最終也對應一個輸出檔案。
我更喜歡把流程分為三個階段。第一階段是準備階段,包括1、2,主角是MapReduce庫,完成拆分作業和拷貝使用者程式等任務;第二階段是運行階段,包括3、4、5、6,主角是使用者定義的map和reduce函數,每個小作業都獨立運行著;第三階段是掃尾階段,這時作業已經完成,作業結果被放在輸出檔案裡,就看使用者想怎麼處理這些輸出了。
5. 詞頻是怎麼統計出來的
結合第四節,我們就可以知道第三節的代碼是如何工作的了。假設咱們定義M=5,R=3,並且有6台機器,一台master。
這幅圖描述了MapReduce如何處理詞頻統計。由於map worker數量不夠,首先處理了分區1、3、4,併產生中間索引值對;當所有中間值都準備好了,Reduce作業就開始讀取對應分區,並輸出統計結果。
6. 使用者的權利使用者最主要的任務是實現map和reduce介面,但還有一些有用的介面是向使用者開放的。
- an input reader。這個函數會將輸入分為M個部分,並且定義了如何從資料中抽取最初的索引值對,比如詞頻的例子中定義檔案名稱和檔案內容是索引值對。
- a partition function。這個函數用於將map函數產生的中間索引值對映射到一個分區裡去,最簡單的實現就是將鍵求雜湊再對R模數。
- a compare function。這個函數用於Reduce作業排序,這個函數定義了鍵的大小關係。
- an output writer。負責將結果寫入底層Distributed File System。
- a combiner function。實際就是reduce函數,這是用於前面提到的最佳化的,比如統計詞頻時,如果每個<w, "1">要讀一次,因為reduce和map通常不在一台機器,非常浪費時間,所以可以在map執行的地方先運行一次combiner,這樣reduce只需要讀一次<w, "n">了。
- map和reduce函數就不多說了。
7. MapReduce的實現目前MapReduce已經有多種實現,除了Google自己的實現外,還有著名的hadoop,區別是Google是c++,而hadoop是用java。另外斯坦福大學實現了一個在多核/多處理器、共用記憶體環境內啟動並執行MapReduce,稱為Phoenix(介紹),相關的論文發表在07年的HPCA,是當年的最佳論文哦!
【轉】Map/Reduce簡介