歡迎光臨
每天分享高質量文章

簡單解釋 MapReduce 演算法

(點擊上方公眾號,可快速關註)

編譯:archimedes

http://www.cnblogs.com/archimedes/p/mapreduce-principle.html

一個有趣的例子

你想數出一摞牌中有多少張黑桃。直觀方式是一張一張檢查並且數出有多少張是黑桃?

MapReduce方法則是:

  1. 給在座的所有玩家中分配這摞牌

  2. 讓每個玩家數自己手中的牌有幾張是黑桃,然後把這個數目彙報給你

  3. 你把所有玩家告訴你的數字加起來,得到最後的結論

拆分

MapReduce合併了兩種經典函式:


1、映射(Mapping)對集合里的每個標的應用同一個操作。即,如果你想把表單里每個單元格乘以二,那麼把這個函式單獨地應用在每個單元格上的操作就屬於mapping。


2、化簡(Reducing )遍歷集合中的元素來傳回一個綜合的結果。即,輸出表單里一列數字的和這個任務屬於reducing。

重新審視上面的例子


重新審視我們原來那個分散紙牌的例子,我們有MapReduce資料分析的基本方法。友情提示:這不是個嚴謹的例子。在這個例子里,人代表計算機,因為他們同時工作,所以他們是個集群。


在大多數實際應用中,我們假設資料已經在每台計算機上了 – 也就是說把牌分發出去並不是MapReduce的一步。(事實上,在計算機集群中如何儲存檔案是Hadoop的真正核心。)


通過把牌分給多個玩家並且讓他們各自數數,你就在並行執行運算,因為每個玩家都在同時計數。這同時把這項工作變成了分佈式的,因為多個不同的人在解決同一個問題的過程中並不需要知道他們的鄰居在乾什麼。


通過告訴每個人去數數,你對一項檢查每張牌的任務進行了映射。 你不會讓他們把黑桃牌遞給你,而是讓他們把你想要的東西化簡為一個數字。

另外一個有意思的情況是牌分配得有多均勻。MapReduce假設資料是洗過的(shuffled)- 如果所有黑桃都分到了一個人手上,那他數牌的過程可能比其他人要慢很多。


如果有足夠的人的話,問一些更有趣的問題就相當簡單了 – 比如“一摞牌的平均值(二十一點演算法)是什麼”。你可以通過合併“所有牌的值的和是什麼”及“我們有多少張牌”這兩個問題來得到答案。用這個和除以牌的張數就得到了平均值。

MapReduce演算法的機制要遠比這複雜得多,但是主體思想是一致的 – 通過分散計算來分析大量資料。無論是Facebook、NASA,還是小創業公司,MapReduce都是目前分析互聯網級別資料的主流方法。

Hadoop中的MapReduce


大規模資料處理時,MapReduce在三個層面上的基本構思


如何對付大資料處理:分而治之


對相互間不具有計算依賴關係的大資料,實現並行最自然的辦法就是採取分而治之的策略


上升到抽象模型:Mapper與Reducer


MPI等並行計算方法缺少高層並行編程模型,為了剋服這一缺陷,MapReduce借鑒了Lisp函式式語言中的思想,用Map和Reduce兩個函式提供了高層的並行編程抽象模型


上升到構架:統一構架,為程式員隱藏系統層細節


MPI等並行計算方法缺少統一的計算框架支持,程式員需要考慮資料儲存、劃分、分發、結果收集、錯誤恢復等諸多細節;為此,MapReduce設計並提供了統一的計算框架,為程式員隱藏了絕大多數系統層面的處理細節


1.對付大資料處理-分而治之


什麼樣的計算任務可進行並行化計算?

並行計算的第一個重要問題是如何劃分計算任務或者計算資料以便對劃分的子任務或資料塊同時進行計算。但一些計算問題恰恰無法進行這樣的劃分!


Nine women cannot have a baby in one month!

例如:Fibonacci函式:  Fk+2 = Fk + Fk+1 

前後資料項之間存在很強的依賴關係!只能串行計算! 

結論:不可分拆的計算任務或相互間有依賴關係的資料無法進行並行計算!

大資料的並行化計算


  一個大資料若可以分為具有同樣計算過程的資料塊,並且這些資料塊之間不存在資料依賴關係,則提高處理速度的最好辦法就是並行計算

  例如:假設有一個巨大的2維資料需要處理(比如求每個元素的開立方),其中對每個元素的處理是相同的,並且資料元素間不存在資料依賴關係,可以考慮不同的劃分方法將其劃分為子陣列,由一組處理器並行處理


 

2.構建抽象模型-Map和Reduce


借鑒函式式設計語言Lisp的設計思想


—函式式程式設計(functional programming)語言Lisp是一種串列處理 語言(List processing),是一種應用於人工智慧處理的符號式語言,由MIT的人工智慧專家、圖靈獎獲得者John McCarthy於1958年設計發明。


—Lisp定義了可對串列元素進行整體處理的各種操作,如:

    如:(add #(1 2 3 4) #(4 3 2 1))   將產生結果: #(5 5 5 5)


—Lisp中也提供了類似於Map和Reduce的操作

    如: (map ‘vector #+ #(1 2 3 4 5)  #(10 11 12 13 14))

    通過定義加法map運算將2個向量相加產生結果#(11 13 15 17 19)

        (reduce #’+ #(11  13  15  17  19)) 通過加法歸併產生累加結果75


Map: 對一組資料元素進行某種重覆式的處理

Reduce: 對Map的中間結果進行某種進一步的結果整


關鍵思想:為大資料處理過程中的兩個主要處理操作提供一種抽象機制


MapReduce中的Map和Reduce操作的抽象描述


MapReduce借鑒了函式式程式設計語言Lisp中的思想,定義瞭如下的Map和Reduce兩個抽象的編程接口,由用戶去編程實現:


—map: (k1; v1) → [(k2; v2)]

輸入:鍵值對(k1; v1)表示的資料

處理:文件資料記錄(如文本檔案中的行,或資料表格中的行)將以“鍵值對”形式傳入map函式;map函式將處理這些鍵值對,並以另一種鍵值對形式輸出處理的一組鍵值對中間結果   [(k2; v2)]

輸出:鍵值對[(k2; v2)]表示的一組中間資料


—reduce: (k2; [v2]) → [(k3; v3)]

輸入: 由map輸出的一組鍵值對[(k2; v2)] 將被進行合併處理將同樣主鍵下的不同數值合併到一個串列[v2]中,故reduce的輸入為(k2; [v2])

處理:對傳入的中間結果串列資料進行某種整理或進一步的處理,並產生最終的某種形式的結果輸出[(k3; v3)] 。

輸出:最終輸出結果[(k3; v3)]


Map和Reduce為程式員提供了一個清晰的操作接口抽象描述


—各個map函式對所劃分的資料並行處理,從不同的輸入資料產生不同的中間結果輸出


—各個reduce也各自並行計算,各自負責處理不同的中間結果資料集合—進行reduce處理之前,必須等到所有的map函式做完,因此,在進入reduce前需要有一個同步障(barrier);這個階段也負責對map的中間結果資料進行收集整理(aggregation & shuffle)處理,以便reduce更有效地計算最終結果


—最終彙總所有reduce的輸出結果即可獲得最終結果


基於MapReduce的處理過程示例–文件詞頻統計:WordCount


設有4組原始文本資料:

Text 1: the weather is good         Text 2: today is good    

Text 3: good weather is good      Text 4: today has good weather


傳統的串行處理方式(Java):


String[] text = new String[] { hello world, hello every one, say hello to everyone in the world ;

HashTable ht = new HashTable();    

for(i = 0; i < 3; ++i) {

    StringTokenizer st = new StringTokenizer(text[i]);

    while (st.hasMoreTokens()) {  

        String word = st.nextToken();        if(!ht.containsKey(word)) {  

            ht.put(word, new Integer(1));

        } else {            int wc = ((Integer)ht.get(word)).intValue() +1;// 計數加1

            ht.put(word, new Integer(wc));

        }

    }

}

for (Iterator itr=ht.KeySet().iterator();  itr.hasNext(); ) {

    String word = (String)itr.next();

    System.out.print(word+ : + (Integer)ht.get(word)+;   );

}

輸出:good:  5;   has: 1;  is: 3;   the: 1;   today: 2;    weather: 3

基於MapReduce的處理過程示例–文件詞頻統計:WordCount


MapReduce處理方式

使用4個map節點:


map節點1:

  輸入:(text1, “the weather is good”)

  輸出:(the, 1), (weather, 1), (is, 1), (good, 1)


map節點2:

  輸入:(text2, “today is good”)

  輸出:(today, 1), (is, 1), (good, 1)


map節點3:

  輸入:(text3, “good weather is good”)

  輸出:(good, 1), (weather, 1), (is, 1), (good, 1)


map節點4:

  輸入:(text3, “today has good weather”)

  輸出:(today, 1), (has, 1), (good, 1), (weather, 1)


使用3個reduce節點:


MapReduce處理方式


MapReduce偽代碼(實現Map和Reduce兩個函式):


Class Mapper method map(String input_key, String input_value):  // input_key: text document name

  // input_value: document contents

  for each word w in input_value:

      EmitIntermediate(w, “1”);

 

Class Reducer method reduce(String output_key, Iterator intermediate_values):

  // output_key: a word

  // output_values: a list of counts

  int result = 0;

  for each v in intermediate_values:

      result += ParseInt(v);

  Emit(output_key, result);

如何提供統一的計算框架


MapReduce提供一個統一的計算框架,可完成:


—計算任務的劃分和調度

—資料的分佈儲存和劃分

—處理資料與計算任務的同步

—結果資料的收集整理(sorting, combining, partitioning,…)

—系統通信、負載平衡、計算性能優化處理

—處理系統節點出錯檢測和失效恢復


MapReduce最大的亮點

—通過抽象模型和計算框架把需要做什麼(what need to do)與具體怎麼做(how to do)分開了,為程式員提供一個抽象和高層的編程接口和框架


—程式員僅需要關心其應用層的具體計算問題,僅需編寫少量的處理應用本身計算問題的程式代碼


—如何具體完成這個並行計算任務所相關的諸多系統層細節被隱藏起來,交給計算框架去處理:從分佈代碼的執行,到大到數千小到單個節點集群的自動調度使用


MapReduce提供的主要功能


—任務調度:提交的一個計算作業(job)將被劃分為很多個計算任務(tasks), 任務調度功能主要負責為這些劃分後的計算任務分配和調度計算節點(map節點或reducer節點); 同時負責監控這些節點的執行狀態, 並負責map節點執行的同步控制(barrier); 也負責進行一些計算性能優化處理, 如對最慢的計算任務採用多備份執行、選最快完成者作為結果


—資料/代碼互定位:為了減少資料通信,一個基本原則是本地化資料處理(locality),即一個計算節點盡可能處理其本地磁盤上所分佈儲存的資料,這實現了代碼向資料的遷移;當無法進行這種本地化資料處理時,再尋找其它可用節點並將資料從網絡上傳送給該節點(資料向代碼遷移),但將盡可能從資料所在的本地機架上尋找可用節點以減少通信延遲


—出錯處理:以低端商用服務器構成的大規模MapReduce計算集群中,節點硬體(主機、磁盤、記憶體等)出錯和軟體有bug是常態,因此,MapReducer需要能檢測並隔離出錯節點,並調度分配新的節點接管出錯節點的計算任務


—分佈式資料儲存與檔案管理:海量資料處理需要一個良好的分佈資料儲存和檔案管理系統支撐,該檔案系統能夠把海量資料分佈儲存在各個節點的本地磁盤上,但保持整個資料在邏輯上成為一個完整的資料檔案;為了提供資料儲存容錯機制,該檔案系統還要提供資料塊的多備份儲存管理能力


—Combiner和Partitioner:為了減少資料通信開銷,中間結果資料進入reduce節點前需要進行合併(combine)處理,把具有同樣主鍵的資料合併到一起避免重覆傳送; 一個reducer節點所處理的資料可能會來自多個map節點, 因此, map節點輸出的中間結果需使用一定的策略進行適當的劃分(partitioner)處理,保證相關資料發送到同一個reducer節點


基於Map和Reduce的並行計算模型


MapReduce的主要設計思想和特征


1、向“外”橫向擴展,而非向“上”縱向擴展(Scale “out”, not “up”)


 即MapReduce集群的構築選用價格便宜、易於擴展的大量低端商用服務器,而非價格昂貴、不易擴展的高端服務器(SMP)—低端服務器市場與高容量Desktop PC有重疊的市場。


因此,由於相互間價格的競爭、可互換的部件、和規模經濟效應,使得低端服務器保持較低的價格—基於TPC-C在2007年底的性能評估結果,一個低端服務器平臺與高端的共享儲存器結構的服務器平臺相比,其性價比大約要高4倍;如果把外存價格除外,低端服務器性價比大約提高12倍—對於大規模資料處理,由於有大量資料儲存需要,顯而易見,基於低端服務器的集群遠比基於高端服務器的集群優越,這就是為什麼MapReduce並行計算集群會基於低端服務器實現


2、失效被認為是常態(Assume failures are common)  


MapReduce集群中使用大量的低端服務器(Google目前在全球共使用百萬台以上的服務器節點),因此,節點硬體失效和軟體出錯是常態,因而:—一個良好設計、具有容錯性的並行計算系統不能因為節點失效而影響計算服務的質量,任何節點失效都不應當導致結果的不一致或不確定性;


任何一個節點失效時,其它節點要能夠無縫接管失效節點的計算任務;當失效節點恢復後應能自動無縫加入集群,而不需要管理員人工進行系統配置—MapReduce並行計算軟體框架使用了多種有效的機制,如節點自動重啟技術,使集群和計算框架具有對付節點失效的健壯性,能有效處理失效節點的檢測和恢復。


3、把處理向資料遷移(Moving processing to the data)


—傳統高性能計算系統通常有很多處理器節點與一些外儲存器節點相連,如用區域儲存網絡(SAN,Storage Area Network)連接的磁盤陣列,因此,大規模資料處理時外存檔案資料I/O訪問會成為一個制約系統性能的瓶頸。


—為了減少大規模資料並行計算系統中的資料通信開銷,代之以把資料傳送到處理節點(資料向處理器或代碼遷移),應當考慮將處理向資料靠攏和遷移。


—MapReduce採用了資料/代碼互定位的技術方法,計算節點將首先將儘量負責計算其本地儲存的資料,以發揮資料本地化特點(locality),僅當節點無法處理本地資料時,再採用就近原則尋找其它可用計算節點,並把資料傳送到該可用計算節點。


4、順序處理資料、避免隨機訪問資料(Process data sequentially and avoid random access)


—大規模資料處理的特點決定了大量的資料記錄不可能存放在記憶體、而只可能放在外存中進行處理。—磁盤的順序訪問和隨即訪問在性能上有巨大的差異

 例:100億(1010)個資料記錄(每記錄100B,共計1TB)的資料庫

   更新1%的記錄(一定是隨機訪問)需要1個月時間;而順序訪問並重寫所有資料記錄僅需1天時間!


—MapReduce設計為面向大資料集批處理的並行計算系統,所有計算都被組織成很長的流式操作,以便能利用分佈在集群中大量節點上磁盤集合的高傳輸帶寬。


為應用開發者隱藏系統層細節(Hide system-level details from the application developer)


—軟體工程實踐指南中,專業程式員認為之所以寫程式困難,是因為程式員需要記住太多的編程細節(從變數名到複雜演算法的邊界情況處理),這對大腦記憶是一個巨大的認知負擔,需要高度集中註意力—而並行程式編寫有更多困難,如需要考慮多執行緒中諸如同步等複雜繁瑣的細節,由於併發執行中的不可預測性,程式的除錯查錯也十分困難;


大規模資料處理時程式員需要考慮諸如資料分佈儲存管理、資料分發、資料通信和同步、計算結果收集等諸多細節問題—MapReduce提供了一種抽象機制將程式員與系統層細節隔離開來,程式員僅需描述需要計算什麼(what to compute), 而具體怎麼去做(how to compute)就交由系統的執行框架處理,這樣程式員可從系統層細節中解放出來,而致力於其應用本身計算問題的演算法設計


平滑無縫的可擴展性(Seamless scalability)


包括兩層意義上的擴展性:資料擴展和系統規模擴展。—理想的軟體演算法應當能隨著資料規模的擴大而表現出持續的有效性,性能上的下降程度應與資料規模擴大的倍數相當—在集群規模上,要求演算法的計算性能應能隨著節點數的增加保持接近線性程度的增長—絕大多數現有的單機演算法都達不到以上理想的要求;


把中間結果資料維護在記憶體中的單機演算法在大規模資料處理時很快失效;從單機到基於大規模集群的並行計算從根本上需要完全不同的演算法設計—奇妙的是,MapReduce幾乎能實現以上理想的擴展性特征。  多項研究發現基於MapReduce的計算性能可隨節點數目增長保持近似於線性的增長

覺得本文有幫助?請分享給更多人

關註「演算法愛好者」,修煉編程內功

淘口令複製以下紅色內容,再打開手淘即可購買

範品社,使用¥極客T恤¥搶先預覽(長按複製整段文案,打開手機淘寶即可進入活動內容)

近期,北京地區正常發貨,但派件時間有所延長。

赞(0)

分享創造快樂