Google MapReduce閱讀筆記

Google MapReduce閱讀筆記


摘要:
MapReduce不是一個產品,它是一種基於分治思想,一種解決問題的思路。Google MapReduce是Google產出的一個編程模型,算法模型,當然Google也有其相關實現,提供給了用戶相關函數接口:
(1)Map函數接口處理一個基於key/value(後簡稱kv)的成對(pair)數據集合,同時也輸出基於kv的數據集合;
(2)Reduce函數接口用來合併Map輸出的kv數據集合;
現實中有許多應用需求都能用這種模型處理,許多應用都能用這種方法解決。

MapReduce架構的程序能在大規模普通PC集羣上實現並行處理,整個集羣系統在程序運行時需要關心:
(1)如何分割數據;
(2)集羣調度;
(3)集羣錯誤處理;
(3)集羣通信;
用戶僅僅提供Map函數接口,Reduce函數接口即可,在MapReduce架構下,能讓那些沒有分佈式計算處理程序開發經驗的程序也有效的利用分佈式集羣的資源。

1.介紹
現實中有許多基於分治的應用需求:
(1)網頁抓取;
(2)日誌處理;
(3)索引倒排;
(4)查詢請求彙總;
(5)…
單機的計算容易理解與完成,但在輸入數據量巨大(TB級別)的情況下,如何能夠在短時間內完成處理呢,只有將這些計算分佈在成百上千的主機上,但此時,並行計算、數據分發、錯誤處理、集羣通訊等等問題綜合到一起,就成爲了一個困難的問題。
爲了解決這個問題,抽象出一個計算模型:該模型下,不必關心並行、容錯、數據分佈、負載均衡等細節,而只需提供Map和Reduce函數。

2.編程模型
MapReduce編程模型原理:
(1)Map函數接受一個kv輸入數據集合,輸出一箇中間kv數據集合;
(2)Reduce函數接受Map輸出的kv數據集合,再進行匯聚。

2.1例子
以統計大量文檔中單詞出現的個數爲例,以下是一段僞代碼,
Map(string $key_doc_name, string $value_doc_content) // 文件名=>文件內容
foreach(string $word in $value_doc_content) // 文件中的單詞
array[$word] ++; // 單詞計數增加

Reduce(string $word, iterator $value) // 單詞=>計數鏈表
int $count = 0;
foreach(int $count in $value) // 計數合併
$count += $value;
emit($word, $count)

很多應用的例子都適合上述MapReduce模型,例如:
(1)分佈式grep:Map輸出匹配行,Reduce直接輸出;
(2)URL訪問頻率計算:Map輸出(URL, 1),Reduce累加,產生(URL, count);

2.2類型
用戶定義的Map和Reduce函數相關類型:
map(k1, v1) => list (k2, v2)
reduce(k2, list(v2)) => list(v2)

3.實現
MapReduce模型有多種實現方式,一種是小型共享內存式,一種是基於NUMA架構的大型多處理器。
Google的實現基於:
(1)x86架構、Linux操作系統、雙核處理器,4G內存;
(2)普通網絡設備,100M或1000M帶寬;
(3)成千上百臺普通PC機器,故障是常態(GFS裏也常提到這個,故障是常態);
(4)存儲爲廉價的IDE硬盤;
(5)用戶提交的作業(job)由系統統一調度:每個工作包含一系列任務(task),調度系統會完成任務的分發、分配;

3.1執行流程
通過將Map的輸入數據集分割爲M個Map片段集合,可將這些Map片段集合分配到多臺機器上執行Map,以實現並行處理;
Map產生的結果數據集又被分配爲R個數據分區,該項工作由 分區函數 完成,分區數量R和分區函數都由用戶指定,如:
hash(key) mod R,下圖展示了MapReduce實現中操作的全部流程:


圖一:MapReduce執行流程
當用戶調用MapReduce時,將發生以下一系列動作:
(1)用戶調用MapReduce,將輸入數據分成M個數據片段集合,然後在集羣中創建大量程序副本(fork);
(2)這些副本中有一個主程序(master),其他均爲工作程序(worker),任務的分配由master完成,它將M個map任務和R個reduce任務分給不同的worker;
(3)被分配到map任務的worker,從數據片段集合中讀取kv對,交給用戶的Map函數處理,生成的結果kv對放在緩存中;
(4)緩存中的kv對通過分區函數,分成R個區域,週期性的寫回本地磁盤,由master再把它們傳給負責reduce的worker進行處理;
(5)負責reduce工作的worker從遠程讀取中間kv數據,對key進行排序,使得相同的key的數據聚合在一起(如果數據量太大,可外部排序);
(6)相同key的中間數據,其value集合交給用戶的reduce函數處理,處理結果寫入對應分區的輸出文件;
(7)所以map和reduce的worker都結束工作後,master喚醒用戶程序,MapReduce調用返回,結果被輸出到了R個文件中。

3.2master
master會存儲一些元信息(GFS的master也用來存元信息),包括每個map和reduce的狀態,是“空閒”、“工作”還是“完成”,它當然也保存各個worker機器的標識;
每個map完成後,master會知道R箇中間kv數據集合的位置,並把這些數據推給reduce處理;
master就像一個管道,R箇中間文件從這個管道從map傳遞給reduce。

3.3容錯
(1)worker失效
master會週期性的ping每個worker,約定時間內仍未收到worker返回的信息,master將標記這個worker失效,這個任務將會分配給其他worker;
失效的worker的狀態會重置爲空閒,等待其他任務調度;
一個map任務如果先被worker A執行,失效後調度給worker B執行,master會將“重新執行”的命令通知給所有reduce的worker,數據將從worker B中讀取;

(2)master失效
一個簡單的方法是,將元數據寫入磁盤,即加如檢查點(checkpoint)。master任務失敗了,由另一個master讀取檢查點,繼續執行。
現實的實現是,只設置一個master進程,master失效就終止MapReduce計算,並通知用戶,可以讓其選擇重新執行。

(3)原子性提交
在用戶提供的map、reduce函數一定,且無出錯的情況下,MapR的產出一定是一樣的,使用“原子性提交”來保證這個特性。
每個map完成時,會產生R個私有臨時文件,該任務完成的通知,以及R個文件的元信息會傳遞給master(如果該map任務有多個worker執行,後續的完成通知將忽略);
每個reduce完成時,會產生1個最終的輸出文件,其文件名唯一(如果該reduce任務有多個worker執行,GFS提供的文件唯一性,或者叫文件重命名原子性將保證數據只有一份);

3.4中間數據的存儲
中間數據的存儲都由GFS(Google File System)管理,GFS保證每個文件按照64M一個block分隔,每個block的副本保存在多臺機器上。
爲了減少數據傳輸成本,map的任務調度會盡量放到副本機器上執行(實在不行,則調度到較近的機器,實施拷貝);
這樣是爲了保證大部分數據都是從本地讀取,減少網絡帶寬,提高運行效率。

3.5任務粒度
map分成M個片段執行,reduce分成了R個片段執行,理論上M和R比worker機器數量多得多,任務分配需要具備負載均衡能力;
worker機器的故障需要有快速恢復能力,故障機器上的任務又能快速分配到其他worker機器上去;

實際上,具體實現中M和R都有一定限制,master必須執行O(M+R)次調度,並保存O(M*R)個狀態(這個很好理解,每個M要產出R份數據);
進一步,R值通常由用戶指定,M根據經驗,一個任務大約處理64M的輸入數據(本地數據存儲優化最有效),通常M=200000,R=5000,worker機器數量=2000。

3.6備用任務
影響一個MapReduce總執行時間,最主要的因素往往是“落伍者”,即短板,常常被稱作“長尾效應”:一臺機器花了很長的時間才處理完最後幾個map或reducer(原因也是多種多樣的,例如硬盤壞了,數據讀取速度奇慢無比等),導致總執行時間超時。
有一個“備用任務”機制解決“長尾效應”,master啓用備用任務完成短板任務。

4.擴展與改進
4.1分區函數
用戶通常會指定reduce任務輸出文件數量R,map產出的中間數據集使用分區函數對數據進行再劃分,之後再輸入到後續任務執行。
默認的分區函數是哈希方法,hash(key) mod R,這種方法能夠平衡負載。有時候用戶有特殊的需求,例如希望每個主機(hostname)的URLs保持在同一個文件中。MapReduce庫支持此類擴展,使用hash(hostname(url)) mod R分區函數就能滿足上述需求。

4.2順序保證
實現保證在給定分區中,kv數據的梳理是按照key增序處理的。

4.3合併函數(Combiner Function)
有時,map產生的中間key的重複數據比重很大,例如詞頻統計的應用,map將產生大量($word, 1)這樣的kv數據,然後reduce把這些記錄累加起來。
可以提供給用戶一個合併函數,在map完成後,本地就做一次合併,生成($word, n),這樣reduce處理的量就會減少很多。
合併函數在每個map任務上都會執行一次,一般來說,合併函數與reduce函數是一樣的,它們的區別是,合併函數執行本地數據合併,結果生成在臨時文件中;reduce函數執行最終的合併,結果生產在最終的文件裏。

4.4輸入輸出類型
文本行是一種最常用的格式:key是偏移量,value是一行的內容;
當然也能夠實現從數據庫裏讀取記錄。

4.5忽略損壞的記錄
某些時候,部分記錄損壞了,MapReduce可以跳過這些記錄,部分數據的損壞可能不影響全局“統計”結果。

4.6計數器
MapReduce庫使用計數器統計不同事件發生次數,例如,用戶想統計已經處理了多少個單詞。
爲了支持這個特性,用戶可在程序中創建命名的計數器對象,在Map和Reduce函數中增加相應的計數器的值。
worker會週期性的把這些計數器的值彙報給master,由master負責這些值的累積(提供查看進度的可能性)。

4.7其他
(1)map和reduce過程中,根據用戶邏輯,可能會產出一些中間文件臨時保存數據,沒有提供類似“兩階段提交”的機制保障這種情況的原子性;
(2)本地執行:分佈式bug難於調試,提供了MapReduce庫的本地實現版本,可以方便的gdb,差錯等;
(3)狀態信息:master使用嵌入式http服務器可以顯示狀態信息:執行進度、已完成任務、成立百分比等;

5.性能測試
5.1集羣配置
(1)1800臺機器;
(2)雙核2GCPU;
(3)4G內存;
(4)160G硬盤;
(5)千兆網卡;
(6)雙層樹形交換網絡,100-200G傳輸帶寬。

5.2grep
5.3排序
5.4備份

結論是:Google MapReduce很牛逼。

n.結束語
爲什麼Google MapReduce能成功:
(1)Google MapReduce庫封裝了並行處理、容錯處理、本地數據優化、負載均衡等技術難點細節,使得庫易於使用;
(2)大量需求能夠通過這種模型解決:檢索、排序、挖掘、機器學習等;
(3)實現並部署了千臺機器組成的MapReduce,Google用它解決了很多問題。

經驗:
(1)約束編程模式使得並行與分佈式計算變得容易,也易於構造容錯計算環境;
(2)網絡帶寬是稀有資源:大量優化用在減少網絡傳輸上;
(3)多次執行相同的任務能減少“長尾”帶了的負面影響,同時解決了機器失效導致的數據丟失問題。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章