10、MapReduce入门

MapReduce定义

Mapreduce是一个分布式运算程序的编程框架,是用户开发“基于hadoop的数据分析应用”的核心框架。
Mapreduce核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在一个hadoop集群上。

MapReduce优缺点

优点

  1. MapReduce 易于编程
    它简单的实现一些接口,就可以完成一个分布式程序,这个分布式程序可以分布到大量廉价的PC机器上运行。也就是说你写一个分布式程序,跟写一个简单的串行程序是一模一样的。就是因为这个特点使得MapReduce编程变得非常流行。
  2. 良好的扩展性
    当你的计算资源不能得到满足的时候,你可以通过简单的增加机器来扩展它的计算能力。
  3. 高容错性
    MapReduce设计的初衷就是使程序能够部署在廉价的PC机器上,这就要求它具有很高的容错性。比如其中一台机器挂了,它可以把上面的计算任务转移到另外一个节点上运行,不至于这个任务运行失败,而且这个过程不需要人工参与,而完全是由Hadoop内部完成的。
  4. 适合PB级以上海量数据的离线处理
    离线处理,说明它适合离线处理而不适合在线处理。比如像毫秒级别的返回一个结果,MapReduce很难做到。

缺点

MapReduce不擅长做实时计算、流式计算、DAG(有向图)计算。

  1. 实时计算
    MapReduce无法像Mysql一样,在毫秒或者秒级内返回结果。
  2. 流式计算
    流式计算的输入数据是动态的,而MapReduce的输入数据集是静态的,不能动态变化。这是因为MapReduce自身的设计特点决定了数据源必须是静态的。
  3. DAG(有向图)计算
    多个应用程序存在依赖关系,后一个应用程序的输入为前一个的输出。在这种情况下,MapReduce并不是不能做,而是使用后,每个MapReduce作业的输出结果都会写入到磁盘,会造成大量的磁盘IO,导致性能非常的低下。

MapReduce核心思想

10、MapReduce入门
mapreduce核心思想、.png

MapReduce进程

一个完整的mapreduce程序在分布式运行时有三类实例进程:

  1. MrAppMaster:负责整个程序的过程调度及状态协调。
  2. MapTask:负责map阶段的整个数据处理流程。
  3. ReduceTask:负责reduce阶段的整个数据处理流程。

MapReduce编程规范

用户编写的程序分成三个部分:Mapper、Reducer和Driver。

  1. Mapper阶段(k-long首字母的偏移量,v-string 记录一行的数据)
    1)用户自定义的Mapper要继承自己的父类
    2)Mapper的输入数据是KV对的形式(KV的类型可自定义)
    3)Mapper中的业务逻辑写在map()方法中
    4)Mapper的输出数据是KV对的形式(KV的类型可自定义)
    5)map()方法(maptask进程)对每一个<K,V>调用一次
  2. Reducer阶段
    (1)用户自定义的Reducer要继承自己的父类
    (2)Reducer的输入数据类型对应Mapper的输出数据类型,也是KV
    (3)Reducer的业务逻辑写在reduce()方法中
    (4)Reducetask进程对每一组相同k的<k,v>组调用一次reduce()方法
  3. Driver阶段
    相当于yarn集群的客户端,用于提交我们整个程序到yarn集群,提交的是封装了mapreduce程序相关运行参数的job对象

WordCount

需求

在给定的文本文件中统计输出每一个单词出现的总次数

数据

数据如下

hello world
hadoop 
spark
hello world
hadoop 
spark
hello world
hadoop 
spark

分析

按照mapreduce编程规范,分别编写Mapper,Reducer,Driver
Mapper工作

  1. 将maptask的文本内容先转换成string
  2. 根据空格将这一行切分成单词
  3. 将单词输出为<单词,1>

Reducer工作

  1. 汇总各个key的个数
  2. 输出该key的总次数

Driver工作

  1. 获取配置信息,获取job对象实例
  2. 指定本程序jar包所在的本地路径
  3. 关联Mapper/Reducer业务类
  4. 指定Mapper数据数据的kv类型
  5. 指定最终数据的数据的kv类型
  6. 指定job的输入原始文件所在目录
  7. 指定job的输出结果所在目录
  8. 提交

添加相关依赖

<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>
</dependencies>

添加log4.properties

在项目的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

编写程序

  1. Mapper类
public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {

    private Text ke = new Text();
    private IntWritable valu = new IntWritable(1);

    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        String[] strings = value.toString().split(" ");
        for(String string : strings) {
            ke.set(string);
            context.write(ke, valu);
        }
    }
}
  1. Reducer类
public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable> {

    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
        int count = 0;
        for(IntWritable value : values) {
            count += value.get();
        }
        context.write(key, new IntWritable(count));
    }
}
  1. Driver类
public class WordCountDriver {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        Configuration configuration = new Configuration();
        Job job = Job.getInstance(configuration);

        job.setJarByClass(WordCountDriver.class);

        job.setMapperClass(WordCountMapper.class);
        job.setReducerClass(WordCountReducer.class);

        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);

        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);

        FileInputFormat.setInputPaths(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));

        boolean success = job.waitForCompletion(true);

        System.exit(success ? 0 : 1);
    }
}

运行测试

本地运行

  1. 在windows环境上配置HADOOP_HOME环境变量
  2. 在idea上运行程序,做如下配置


    10、MapReduce入门
    本地运行wordcount.png

集群上测试

  1. 将程序打成jar包,然后拷贝到hadoop集群中
  2. 启动hadoop集群
  3. 执行WordCount程序
    hadoop jar demo-1.0-SNAPSHOT.jar com.zj.mapreduce.wordcount.WordCountDriver /user/hadoop/wordcount/input/ /user/hadoop/wordcount/output/


    10、MapReduce入门
    集群运行wordcount.png