十、MapReduce概述


一、 MapReduce定義

        MapReduce 是一個分佈式運算程序的編程框架, 是用戶開發 ‘基於Hadoop的數據分析應用’ 的核心框架。

        MapReduce 核心功能是將 用戶編寫的業務邏輯代碼 和 自帶默認組件 整合成一個完成的 分佈式運算程序, 併發運行在一個 Hadoop 集羣上。


二、 MapReduce 優缺點

2.1 優點

  1. MapReduce 易於編程
    它簡單的實現了一些接口, 就可以完成一個分佈式程序, 這個分佈式程序可以分佈到大量廉價的 PC機器上運行。 也就是說你寫一個分佈式程序, 跟寫一個簡單的串行程序是一模一樣的。 就是因爲這個特點使得 MapReduce 編程變得非常流行

  2. 良好的擴展性
    當你的計算資源不能得到滿足的時候, 你可以通過簡單的增加機器 來擴展它的計算能力。

  3. 高容錯性
    MapReduce 設計的初衷就是使程序能夠部署到廉價的 PC 機器上, 這就要求它具有很高的容錯性。 比如 其中一臺機器掛了,它可以把上面的計算任務轉移到另一個節點上運行, 不至於這個任務運行失敗, 而且這個過程不需要人工參與, 而完全是由 Hadoop內部完成的。

  4. 適合 PB級以上海量數據的離線處理
    可以實現上千臺服務器集羣併發工作, 提供數據處理能力。

2.2 缺點

  1. 不擅長實時計算
    MapReduce 無法像 MySQL那樣, 在毫秒或者秒級內返回結果。
  2. 不擅長流式計算
    流式計算的輸入數據是動態的, 而 MapReduce 的輸入數據是靜態的, 不能動態變化。 這是因爲 MapReduce自身的設計特點決定了數據源必須是靜態的。
  3. 不擅長DAG(有向圖) 計算
    多個應用程序存在依賴關係, 後一個應用程序的輸入爲前一個的輸出。 在這種情況下, MapReduce 並不是不能做, 而是使用後, 每個 MapReduce 作業的輸出結果都會寫入到磁盤中, 會造成大量的磁盤IO, 導致性能非常低下。

三、 MapReduce 核心思想

在這裏插入圖片描述

  1. 分佈式的運算程序往往需要分成至少2個階段。
  2. 第一個階段的MapTask併發實例,完全並行運行,互不相干。
  3. 第二個階段的ReduceTask併發實例互不相干,但是他們的數據依賴於上一個階段的所有MapTask併發實例的輸出。
  4. MapReduce編程模型只能包含一個Map階段和一個Reduce階段,如果用戶的業務邏輯非常複雜,那就只能多個MapReduce程序,串行運行。

總結:分析WordCount數據流走向深入理解MapReduce核心思想。


四、MapReduce 進程

一個完整的 MapReduce 程序在分佈式運行時有三類實例進程:

  1. MrAPPMaster: 負責整個程序的過程調度及狀態協調;
  2. MapTask:負責Map 階段的整個數據處理流程;
  3. ReduceTask:負責Reduce 階段的整個數據處理流程。

五、官方 WordCount 源碼

採用反編譯工具反編譯源碼,發現WordCount案例有Map類、Reduce類和驅動類。且數據的類型是Hadoop自身封裝的序列化類型


六、常用的數據序列化類型

Java類型 Hadoop Writable類型
Boolean BooleanWritable
Byte ByteWritable
Int IntWritable
Float FloatWritable
Long LongWritable
Double DoubleWritable
String Text
Map MapWritable
Array ArrayWritable

七、 MapReduce編程規範

用戶編寫的程序分成三個部分:Mapper、Reducer和Driver。

  1. Mapper 階段
    ① 用戶自定義的 mapper 要繼承自己的父類
    ② Mapper 的輸入數據是 KV對的形式(KV 的類型可自定義)
    ③ Mapper 中的業務邏輯寫在 map() 方法中
    ④ Mapper 的輸出數據是 KV 對的形式 (KV 的類型可自定義)
    ⑤ map() 方法 (mapTask進程) 對每個<K,V> 調用一次
  2. Reduce 階段
    ① 用戶自定義的 Reduce 要繼承自己的父類
    ② Reduce 的輸入數據類型對應 Mapper 的輸出數據類型, 也是 KV 鍵值對
    ③ Reduce 的業務邏輯寫在 reduce() 方法中
    ④ ReduceTask 進程對每一組相同的 k的 <k,v> 組調用一次 reduce() 方法
  3. Driver 階段
    相當於 YARN 集羣的客戶端, 用於提交我們整個程序要 YARN 集羣, 提交的是封裝了 MapReduce 程序相關運行參數的job對象

八、 WordCount 案例實操

8.1 需求

在給定的文本文件中統計輸出每一個單詞出現的總次數

  1. 輸入數據(wordCount.txt)

    kino Like zhu
    Hadoop
    hi hi hi
    Word Count
    
  2. 期望輸出數據

    Count	1
    Hadoop	1
    Like	1
    Word	1
    hi	3
    kino	1
    zhu	1
    

8.2 需求分析

按照 MapReduce 編程規範, 分別編寫 Mapper、Reducer、Driver

在這裏插入圖片描述

8.3 準備環境

  1. 創建 Maven 工程

  2. 添加 pom.xml 依賴

    <dependencies>
       <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.8.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-common</artifactId>
            <version>2.7.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>2.7.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-hdfs</artifactId>
            <version>2.7.2</version>
        </dependency>
        <dependency>
            <groupId>jdk.tools</groupId>
            <artifactId>jdk.tools</artifactId>
            <version>1.8</version>
            <scope>system</scope>
            <systemPath>${JAVA_HOME}/lib/tools.jar</systemPath>
        </dependency>
    </dependencies>
    
  3. 在項目的 src/main/resources 目錄下, 創建名爲 “log4j.properties” 的文件並填入一下內容

    log4j.rootLogger=INFO, stdout
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
    log4j.appender.logfile=org.apache.log4j.FileAppender
    log4j.appender.logfile.File=target/spring.log
    log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
    log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n 
    
  4. 編寫程序

    1. 編寫 mapper 類

      package com.kino.mapreduce.wordcount;
      
      import org.apache.hadoop.io.IntWritable;
      import org.apache.hadoop.io.LongWritable;
      import org.apache.hadoop.io.Text;
      import org.apache.hadoop.mapreduce.Mapper;
      
      import java.io.IOException;
      
      /**
       * Map 階段
       *
       * 需要繼承 Mapper, 並重寫 map方法, 完成自定義的 Mapper
       *
       * KEYIN: 輸入的 key 類型,
       * VALUEIN: 輸入的 Value 類型,
       * KEYOUT: 輸出的 key 類型,
       * VALUEOUT: 輸出的 Value 類型,
       */
      public class WordcountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
      
          Text k = new Text();
          IntWritable v = new IntWritable(1);
      
          /**
           *
           * @param key: 每一行的 Key
           * @param value: 每一行的 value
           * @param context: 全局上下文對象
           * @throws IOException
           * @throws InterruptedException
           */
          @Override
          protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
              //1. 獲取一行數據
              String line = value.toString();
      
              //2. 切割數據
              String[] words = line.split(" ");
      
              for (String word : words) {
                  k.set(word);
                  context.write(k, v);
              }
          }
      }	
      
    2. 編寫 Reducer 類

      package com.kino.mapreduce.wordcount;
      
      import org.apache.hadoop.io.IntWritable;
      import org.apache.hadoop.io.Text;
      import org.apache.hadoop.mapreduce.Reducer;
      
      import java.io.IOException;
      
      /**
       * Reduce 階段
       *
       * 通過繼承 Reduce, 重寫 reduce 方法, 完成自定義的Reducer
       */
      public class WordcountReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
      
          IntWritable v = new IntWritable();
      
          @Override
          protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
              //1. 將相同 Key 的value 進行彙總
              int sum = 0;
              for (IntWritable value : values) {
                  sum += value.get();
              }
              //2. 寫出
              v.set(sum);
              context.write(key, v);
          }
      }
      
    3. 編寫 Driver 驅動類

      package com.kino.mapreduce.wordcount;
      
      import org.apache.hadoop.conf.Configuration;
      import org.apache.hadoop.fs.Path;
      import org.apache.hadoop.io.IntWritable;
      import org.apache.hadoop.io.Text;
      import org.apache.hadoop.mapreduce.Job;
      import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
      import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
      
      import java.io.IOException;
      
      /**
       * Created by kino on 2019/7/22 to WorkSpace
       */
      public class WordcountDriver {
      
          public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
              Configuration conf = new Configuration();
              //1. 獲取 Job 對象   一個 MapReduce 程序實際上就是一個 Job
              Job job = Job.getInstance(conf);
      
              //2. 關聯 jar
              job.setJarByClass(WordcountDriver.class);
      
              //3. 關聯當前 Job 對應的 Mapper 和 Reduce
              job.setMapperClass(WordcountMapper.class);
              job.setReducerClass(WordcountReducer.class);
      
              //4. 設置 Mapper 輸出的 Key 和 Value 類型
              job.setMapOutputKeyClass(Text.class);
              job.setMapOutputValueClass(IntWritable.class);
      
              //5. 設置最終輸出的 Key 和 Value 類型
              job.setOutputKeyClass(Text.class);
              job.setOutputValueClass(IntWritable.class);
      
              //6. 設置文件的輸入 和 結果的輸出位置
              FileInputFormat.setInputPaths(job, new Path("D:\\wordCount.txt"));
              FileOutputFormat.setOutputPath(job, new Path("D:\\output"));
      
              //7. 提交 Job
              boolean b = job.waitForCompletion(true);
              System.exit(b ? 0 : 1);
          }
      
      }
      
    4. 運行測試, 最終將在 FileOutputFormat.setOutputPath 指定的路徑下生成 4 個文件
      在這裏插入圖片描述
      其中 part-r-00000 就是程序輸出的結果

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