標籤:style blog http io os 使用 ar java for
江湖傳說永流傳:Google技術有"三寶",GFS、MapReduce和大表(BigTable)!
Google在03到06年間連續發表了三篇非常有影響力的文章,各自是03年SOSP的GFS,04年OSDI的MapReduce,和06年OSDI的BigTable。SOSP和OSDI都是作業系統領域的頂級會議,在電腦學會推薦會議裡屬於A類。SOSP在單數年舉辦,而OSDI在雙數年舉辦。
那麼這篇部落格就來介紹一下MapReduce。
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函數的核心代碼很簡短,主要就是實現這兩個函數。
map(String key, String value):// key: document name// value: document contentsfor each word w in value:EmitIntermediate(w, "1");reduce(String key, Iterator values):// key: a word// values: a list of countsint 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,是當年的最佳論文哦!參考文獻[1] MapReduce : Simplified Data Processing on Large Clusters. In proceedings of OSDI‘04.[2] Wikipedia. http://en.wikipedia.org/wiki/Mapreduce[3] Phoenix. http://mapreduce.stanford.edu/[4] Evaluating MapReduce for Multi-core and Multiprocessor Systems. In proceedings of HPCA‘07.
Google技術"三寶"之MapReduce