Hadoop學習筆記—8.Combiner與自定義Combiner

一、Combiner的出現背景

1.1 回顧Map階段五大步驟

  在第四篇博文《初識MapReduce》中,我們認識了MapReduce的八大步湊,其中在Map階段總共五個步驟,如下圖所示:

map section

  其中,step1.5是一個可選步驟,它就是我們今天需要了解的?Map規約?階段。現在,我們再來看看前一篇博文《計數器與自定義計數器》中的第一張關于計數器的圖:


  我們可以發現,其中有兩個計數器:Combine output recordsCombine input records,他們的計數都是0,這是因為我們在代碼中沒有進行Map階段的規約操作。

1.2 為什么需要進行Map規約操作

  眾所周知,Hadoop框架使用Mapper將數據處理成一個個的<key,value>鍵值對,在網絡節點間對其進行整理(shuffle),然后使用Reducer處理數據并進行最終輸出。

  在上述過程中,我們看到至少兩個性能瓶頸

  (1)如果我們有10億個數據,Mapper會生成10億個鍵值對在網絡間進行傳輸,但如果我們只是對數據求最大值,那么很明顯的Mapper只需要輸出它所知道的最大值即可。這樣做不僅可以減輕網絡壓力,同樣也可以大幅度提高程序效率。

  總結:網絡帶寬嚴重被占降低程序效率;

  (2)假設使用美國專利數據集中的國家一項來闡述數據傾斜這個定義,這樣的數據遠遠不是一致性的或者說平衡分布的,由于大多數專利的國家都屬于美國,這樣不僅Mapper中的鍵值對、中間階段(shuffle)的鍵值對等,大多數的鍵值對最終會聚集于一個單一的Reducer之上,壓倒這個Reducer,從而大大降低程序的性能。

  總結:單一節點承載過重降低程序性能;

  那么,有木有一種方案能夠解決這兩個問題呢?

二、初步探索Combiner

2.1 Combiner的橫空出世

  在MapReduce編程模型中,在Mapper和Reducer之間有一個非常重要的組件,它解決了上述的性能瓶頸問題,它就是Combiner

PS:

①與mapper和reducer不同的是,combiner沒有默認的實現,需要顯式的設置在conf中才有作用。

②并不是所有的job都適用combiner,只有操作滿足結合律的才可設置combiner。combine操作類似于:opt(opt(1, 2, 3), opt(4, 5, 6))。如果opt為求和、求最大值的話,可以使用,但是如果是求中值的話,不適用。

  每一個map都可能會產生大量的本地輸出Combiner的作用就是對map端的輸出先做一次合并,以減少在map和reduce節點之間的數據傳輸量,以提高網絡IO性能,是MapReduce的一種優化手段之一,其具體的作用如下所述。

  (1)Combiner最基本是實現本地key的聚合,對map輸出的key排序,value進行迭代。如下所示:

  map: (K1, V1) → list(K2, V2)?
  combine: (K2, list(V2)) → list(K2, V2)?
  reduce: (K2, list(V2)) → list(K3, V3)

  (2)Combiner還有本地reduce功能(其本質上就是一個reduce),例如Hadoop自帶的wordcount的例子和找出value的最大值的程序,combiner和reduce完全一致,如下所示:

  map: (K1, V1) → list(K2, V2)?
  combine: (K2, list(V2)) → list(K3, V3)?
  reduce: (K3, list(V3)) → list(K4, V4)

PS:現在想想,如果在wordcount中不用combiner,那么所有的結果都是reduce完成,效率會相對低下。使用combiner之后,先完成的map會在本地聚合,提升速度。對于hadoop自帶的wordcount的例子,value就是一個疊加的數字,所以map一結束就可以進行reduce的value疊加,而不必要等到所有的map結束再去進行reduce的value疊加。

2.2 融合Combiner的MapReduce

  前面文章中的代碼都忽略了一個可以優化MapReduce作業所使用帶寬的步驟—Combiner,它在Mapper之后Reducer之前運行。Combiner是可選的,如果這個過程適合于你的作業,Combiner實例會在每一個運行map任務的節點上運行。Combiner會接收特定節點上的Mapper實例的輸出作為輸入,接著Combiner的輸出會被發送到Reducer那里,而不是發送Mapper的輸出。Combiner是一個“迷你reduce”過程,它只處理單臺機器生成的數據

2.3 使用MyReducer作為Combiner

  在前面文章中的WordCount代碼中加入以下一句簡單的代碼,即可加入Combiner方法:

    // 設置Map規約Combinerjob.setCombinerClass(MyReducer.class);

  還是以下面的文件內容為例,看看這次計數器會發生怎樣的改變?

  (1)上傳的測試文件的內容

hello edison
hello kevin

  (2)調試后的計數器日志信息

  可以看到,原本都為0的Combine input records和Combine output records發生了改變。我們可以清楚地看到map的輸出和combine的輸入統計是一致的,而combine的輸出與reduce的輸入統計是一樣的。由此可以看出規約操作成功,而且執行在map的最后,reduce之前。

三、自己定義Combiner

  為了能夠更加清晰的理解Combiner的工作原理,我們自定義一個Combiners類,不再使用MyReduce做為Combiners的類,具體的代碼下面一一道來。

3.1 改寫Mapper類的map方法

復制代碼
  public static class MyMapper extendsMapper<LongWritable, Text, Text, LongWritable> {protected void map(LongWritable key, Text value,Mapper<LongWritable, Text, Text, LongWritable>.Context context)throws java.io.IOException, InterruptedException {String line = value.toString();String[] spilted = line.split(" ");for (String word : spilted) {context.write(new Text(word), new LongWritable(1L));// 為了顯示效果而輸出Mapper的輸出鍵值對信息System.out.println("Mapper輸出<" + word + "," + 1 + ">");}};}
復制代碼

3.2 改寫Reducer類的reduce方法

復制代碼
     public static class MyReducer extendsReducer<Text, LongWritable, Text, LongWritable> {protected void reduce(Text key,java.lang.Iterable<LongWritable> values,Reducer<Text, LongWritable, Text, LongWritable>.Context context)throws java.io.IOException, InterruptedException {// 顯示次數表示redcue函數被調用了多少次,表示k2有多少個分組System.out.println("Reducer輸入分組<" + key.toString() + ",N(N>=1)>");long count = 0L;for (LongWritable value : values) {count += value.get();// 顯示次數表示輸入的k2,v2的鍵值對數量System.out.println("Reducer輸入鍵值對<" + key.toString() + ","+ value.get() + ">");}context.write(key, new LongWritable(count));};}
復制代碼

3.3 添加MyCombiner類并重寫reduce方法

復制代碼
    public static class MyCombiner extendsReducer<Text, LongWritable, Text, LongWritable> {protected void reduce(Text key,java.lang.Iterable<LongWritable> values,org.apache.hadoop.mapreduce.Reducer<Text, LongWritable, Text, LongWritable>.Context context)throws java.io.IOException, InterruptedException {// 顯示次數表示規約函數被調用了多少次,表示k2有多少個分組System.out.println("Combiner輸入分組<" + key.toString() + ",N(N>=1)>");long count = 0L;for (LongWritable value : values) {count += value.get();// 顯示次數表示輸入的k2,v2的鍵值對數量System.out.println("Combiner輸入鍵值對<" + key.toString() + ","+ value.get() + ">");}context.write(key, new LongWritable(count));// 顯示次數表示輸出的k2,v2的鍵值對數量System.out.println("Combiner輸出鍵值對<" + key.toString() + "," + count+ ">");};}
復制代碼

3.4 添加設置Combiner的代碼

    // 設置Map規約Combinerjob.setCombinerClass(MyCombiner.class);

3.5 調試運行的控制臺輸出信息

  (1)Mapper

Mapper輸出<hello,1>
Mapper輸出<edison,1>
Mapper輸出<hello,1>
Mapper輸出<kevin,1>

  (2)Combiner

復制代碼
Combiner輸入分組<edison,N(N>=1)>
Combiner輸入鍵值對<edison,1>
Combiner輸出鍵值對<edison,1>
Combiner輸入分組<hello,N(N>=1)>
Combiner輸入鍵值對<hello,1>
Combiner輸入鍵值對<hello,1>
Combiner輸出鍵值對<hello,2>
Combiner輸入分組<kevin,N(N>=1)>
Combiner輸入鍵值對<kevin,1>
Combiner輸出鍵值對<kevin,1>
復制代碼

  這里可以看出,在Combiner中進行了一次本地的Reduce操作,從而簡化了遠程Reduce節點的歸并壓力。

  (3)Reducer

Reducer輸入分組<edison,N(N>=1)>
Reducer輸入鍵值對<edison,1>
Reducer輸入分組<hello,N(N>=1)>
Reducer輸入鍵值對<hello,2>
Reducer輸入分組<kevin,N(N>=1)>
Reducer輸入鍵值對<kevin,1>

  這里可以看出,在對hello的歸并上,只進行了一次操作就完成了。

  那么,如果我們再來看看不添加Combiner時的控制臺輸出信息:

  (1)Mapper

Mapper輸出<hello,1>
Mapper輸出<edison,1>
Mapper輸出<hello,1>
Mapper輸出<kevin,1>

  (2)Reducer

復制代碼
Reducer輸入分組<edison,N(N>=1)>
Reducer輸入鍵值對<edison,1>
Reducer輸入分組<hello,N(N>=1)>
Reducer輸入鍵值對<hello,1>
Reducer輸入鍵值對<hello,1>
Reducer輸入分組<kevin,N(N>=1)>
Reducer輸入鍵值對<kevin,1>
復制代碼

  可以看出,沒有采用Combiner時hello都是由Reducer節點來進行統一的歸并,也就是這里為何會有兩次hello的輸入鍵值對了。

總結:從控制臺的輸出信息我們可以發現,其實combine只是把兩個相同的hello進行規約,由此輸入給reduce的就變成了<hello,2>。在實際的Hadoop集群操作中,我們是由多臺主機一起進行MapReduce的,如果加入規約操作,每一臺主機會在reduce之前進行一次對本機數據的規約,然后在通過集群進行reduce操作,這樣就會大大節省reduce的時間,從而加快MapReduce的處理速度。

參考資料

(1)萬川梅、謝正蘭,《Hadoop應用開發實戰詳解(修訂版)》:http://item.jd.com/11508248.html

(2)Suddenly,《Hadoop日記Day17-計數器、map規約與分區學習》:http://www.cnblogs.com/sunddenly/p/4009568.html

(3)guoery,《MapReduce中Combiner的使用及誤區》:http://blog.csdn.net/guoery/article/details/8529004

(4)iPolaris,《Hadoop中Combiner的使用》:http://blog.csdn.net/ipolaris/article/details/8723782

?

轉載于:https://www.cnblogs.com/zzmmyy/p/7777286.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/251585.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/251585.shtml
英文地址,請注明出處:http://en.pswp.cn/news/251585.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

6-12mysql庫的操作

1&#xff0c;mysql庫的各種分類: nformation_schema&#xff1a; 虛擬庫&#xff0c;不占用磁盤空間&#xff0c;存儲的是數據庫啟動后的一些參數&#xff0c;如用戶表信息、列信息、權限信息、字符信息等.  performance_schema&#xff1a; MySQL 5.5開始新增一個數據庫&am…

css --- 行內框和內容區

css規定font-size的大小實際上是字體的高度 可以將內容區理解為font-size的大小. 行內高可以理解為 ( (line-height) - (font-size) ) /2 然后再font-size 的上下加上前面的值 看下面的例子 <p style"font-size:12px;line-height:12px;">this is text, <em&…

DotNetTextBox V3.0 所見即所得編輯器控件 For Asp.Net2.0(ver 3.0.7Beta) 增加多語言!

英文名&#xff1a;DotNetTextBox V3.0 WYSWYG Web Control For Asp.Net2.0 中文名&#xff1a;DotNetTextBox V3.0 所見即所得編輯器控件 For Asp.Net2.0 類型: 免費控件(保留版權) 作者: 小寶.NET 2.0(Terry Deng) 主頁&#xff1a;http://www.aspxcn.com.cn 控件演示頁面: h…

phantomjs

npm 安裝 phantomjs失敗&#xff0c;解決辦法是到http://phantomjs.org/download.html 下載需要的壓縮包&#xff0c;然后放到%appData%\Local\Temp\phantomjs\下&#xff0c;重新執行npm i 轉載于:https://www.cnblogs.com/tellme/p/7777626.html

js動態刪除行錯誤

Uncaught TypeError: Failed to execute removeChild on Node: parameter 1 is not of type Node. js中出現如上錯誤&#xff0c;檢查驗證document.getElementById&#xff08;&#xff09;中的參數是否傳遞正確&#xff0c; 例&#xff1a; var textnumber parseInt(docume…

css --- 浮動元素與 塊框/行內框重疊時的細節

塊框,可以認為是塊級元素(如div、h1)的內容區 內邊距 行內框可以認為是行內元素(如span)的內容區 內邊距 當 塊級框/行內框 和一個浮動元素重疊時&#xff0c;行內框的邊框、背景和內容都在幅度元素之上&#xff0c;塊級框的邊框和背景都在浮動元素的下面&#xff0c;但內容在…

Android 禁止Viewpager左右滑動功能

做項目要求某種情況下ViewPager不能滑動 百度后發現重寫ViewPager&#xff0c;覆蓋ViewPager的onInterceptTouchEvent(MotionEvent arg0)方法和onTouchEvent(MotionEvent arg0)方法&#xff0c;這兩個方法的返回值都是boolean類型的&#xff0c;只需要將返回值改為false&#x…

error C1853: “Debug\BigBuffer.pch”預編譯頭文件來自編譯器的早期版本,或者預編譯頭為 C++ 而在 C 中使用它(或相反)...

<pre id"best-content-1299104064" mb-10""" style"font-size: 14px; line-height: 28px; ">該錯誤是因為當項目中混合了 .cpp 和 .c 文件時&#xff0c;編譯器會對它們采取不同的編譯方式&#xff08;主要是因為對函數聲明的處理方式…

6.13的練習

#&#xff01;Usr/bin/env python # -*- coding:utf-8 -*- # Author:Alex Li 一個整數&#xff0c;它加上100后是一個完全平方數&#xff0c;再加上268又是一個完全平方數&#xff0c;請問該數是多少&#xff1f;&#xff1a;for i in range(1,1000):for j in range(1,1000):i…

npm --- 包的發布與導入

安裝好NODE后,下面演示如何編寫一個包,并將其發布到NPM倉庫中,最后通過NPM安裝回本地. 以下例子是在windows*64環境下運行的. 1.編寫模塊 exports.sayHello function(){return Hello World; }將上述代碼保存在hello.js中 2.初始化包描述文件: 使用npm init指令,快速生成包…

賬號注冊的form

class RegForm(forms.ModelForm):password forms.CharField(widgetforms.PasswordInput, label密碼, min_length6) # 重寫默認字段re_password forms.CharField(widgetforms.PasswordInput, label確認密碼, min_length6) # 新增字段class Meta:model models.UserProfile …

servlet面試常考 (轉載)

編輯 刪除 1、說一說Servlet生命周期&#xff08;非常重要&#xff09; Servlet生命周期包括三部分&#xff1a; 初始化&#xff1a;Web容器加載servlet&#xff0c;調用init()方法…

XPath 的使用

XPath 的使用 XPath&#xff0c;全稱XML Path Language&#xff0c;即XML路徑語言&#xff0c;它是一門在XML文檔中查找信息的語言&#xff0c;最初用于搜尋XML文檔&#xff0c;但是也同樣適用于HTML文檔的搜索。前面我們在解析或抽取網頁信息時&#xff0c;使用的是正則表達式…

Node --- EventProxy的原理

EventProxy來自于Backbone的事件模塊,Backbone的事件模塊是Model、View模塊的基礎功能&#xff0c;在前端有廣泛的使用。它在每個非all事件觸發時都會觸發一次all事件&#xff0c;相關代碼如下: // Trigger an event, firing all bound callbacks. Callbacks are passed the /…

spring項目啟動執行特定方法

1. 方法上加注解PostConstructCompantpublic class InitDemo{ PostConstruct public void init(){ //項目啟動就會執行這個方法 doSomething(); }}2.xml配置init-method<bean id"InitDemo" class"com.xxx.InitDemo" scope"singleton" init-me…

WinCC歸檔數據報表控件

1、背景 WinCC實現報表歷來是老大難&#xff0c;自帶的報表功能不好使&#xff0c;又沒有好用的第三方控件。雖然網上也有很多實現報表的方法&#xff0c;但是毫無例外的要求使用者具有腳本編程功底&#xff0c;HwDataReport的出現將終結這一現象。您無需一行腳本即可完成…

vue數組操作不更新視圖問題

vue 觀察數組的變異方法 更新視圖 push&#xff08;&#xff09; pop() shift() unshift() splice(i,n,arr) sort(xx) reverse() ex: app.book.push({ name:css, author:lee }) 有些方法不會改變數組 filter() concat() slice() 返回新數組 需要用 新返回的數組 更新原數組 ap…

java中如何計算兩個時間段的月份差

直接計算&#xff0c;先取得兩個日期的年份和月份&#xff0c;月份差&#xff08;第二年份-第一年份&#xff09;*12 第二月份-第一月份轉載于:https://www.cnblogs.com/pretty-guy/p/3284593.html

Node --- Promise中的多異步協作

當我們需要處理多個異步調用時,應該如何處理呢? //首先假設有2個讀取文件的異步調用,promise1和promise2 var promise1 readFile ("foo.txt", "utf-8"); var promise2 readFile ("bar.txt", "uft-8");//然后,我們可以使用all()方法…

Software-OO 面向對象思維

2017-11-06 11:02:50 所有編程語言的最終目的都是提供一種“抽象”方法。 解決問題的復雜程度直接取決于抽象的種類及質量。這兒的“種類”是指準備對什么進行“抽象”&#xff1f; 匯編是對基礎機器的少量抽象。“命令式”語言是對匯編語言的一種抽象。 &#xff08;Alan Kay …