大数据(八):MapReduce入门与简单实现

一、MapReduce定义

        MapReduce是一个分布式运算程序的编程框架,是用户开发“基于hadoop的数据分析应用”的核心框架。

        MapReduce核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在一个hadoop集群上。

二、MapReduce优缺点

优点:

  1. MapReduce易于编程。它简单的实现一些接口,就可以完成一个分布式程序,这个分布式程序可以分布到大量廉价的pc机器上运行。依旧是说写一个分布式程序,跟写一个简单的串行程序是一样的。

  2. 良好的扩展性。当计算资源不能得到满足的时候,可以通过简单的增加机器来扩展它的计算能力。

  3. 高容错性。MapReduce设计的初衷就是使程序能够部署在廉价机器上, 这就要求它具有很高的容错性,不如其中一台机器挂了,它可以把上面的计算任务转移到另外一个节点上运行,不至于这个任务运行失败,而且这个过程不需要人工参与,而完全是由hadoopp内部完成。

  4. 适合PB级以上海量数据的离线处理。不适合实施反馈处理,在毫秒级返回一个结果,MapReduce很难做到

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

  1. 实时计算。MapReduce无法像MySQL一样,在毫秒或者秒级内返回结果。

  2. 流式计算。流式计算的输入数据是动态的,而MapReduce的输入数据集是静态的,不能动态变化。这是应为MapReduce自身的设计特点决定了数据源必须是静态的。

  3. DAG(有向图)计算。多个应用程序存在依赖关系,后一个应用程序的输入为前一个的输出。这种情况下,MapReduce并不是不能做,而是使用后,每个MapReduce作业的输出结果都会写入到磁盘,会造成大量的磁盘IO,导致性能非常的低下。

三、MapReduce核心编程思想

四、MapReduce进程

一个完整的MapReduce程序在分布式运行时有三类实际进程。

  1. MrAppMaster:负责整个程序的过程调度及状态协调。

  2. MapTask:负责Map阶段的整个数据处理流程。

  3. ReduceTask:负责reduce阶段的整个数据处理流程。

五、MapReduce编程规范(八股文)

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

  1. Mapper阶段

    1. 用户自定义的Mapper要继承自己的父类(继承Mapper类)

    2. Mapper的输入参数是KV对的形式

    3. Mapper中的业务逻辑写在Map方法中

    4. Mapper的输出数据是KV对的形式

    5. map()方法(maptask进程)对每个KV对调用一次

  2. Reducer阶段

    1. 用户自定义的Reducer要继承自己的父类(继承Reducer类)

    2. Reducer的输入数据类型对应Mapper的输出数据类型

    3. Reducer的业务逻辑编写在reduce()方法中

    4. ReduceTask进程对每一组相同K的KV对调用一次reduce()方法

  3. Driver阶段

    1. 整个程序需要一个Driver来进行提交。提交的是一个描述了各种必要信息的job对象

六、Hadoop序列化

1.为什么要序列化?

        一般来说,“活的”对象只生存在内存中,关机断电后就没有了。而且“活的对象只能由本地的进程使用,不能被发送到网络上的另外一台计算机。然而序列化可以存储“活的”对象,可以将“活的”对象发送到远程计算机。

2.什么是序列化?

        序列化就是内存中的对象,转换成字节序列(或者其他数据传输协议)以便于储存(持久化)和网络传输。

        反序列化就是将收到的字节序列(或者其他数据传输协议)或者是硬盘的持久化数据,转成内存中的对象。

3.为什么不使用java的序列化?

        Java的序列化是一个重要级序列化框架(Serializable),一个对象被序列化后,会附带很多额外的信息(各种校验信息,header,继承体系等),不便于在网络中高效传输。所以,hadoop自己开发了一套序列化机制(Writable),精简、高效。

4.为什么序列化对Hadoop很重要?

        因为Hadoop在集群之间进行通讯或者RPC调用的时候,需要序列化,而且要求序列化要快,且体积要小,占用带宽要小。

        序列化和反序列化在分布式数据处理领域经常出现:进程通讯和永久存储。然而Hadoop中各个节点的通讯都是通过远程调用(RPC)实现的,RPC序列化要求具有以下几个特点:

  1. 紧凑:紧凑的格式能让我们充分利用网络带宽,而带宽是数据中心最稀罕的资源。

  2. 快速:进程通信形成了分布式系统的骨架,所以需要尽量减少序列化和反序列化的性能开销。

  3. 可扩展:协议为了满足新的需求变化,所以控制客服端和服务器过程中,需要直接引用相应的协议,这些是新协议,原序列化方式能支持新的协议报文。

  4. 互操作:能支持不同语言写的客户端和服务器交互。

5.常用序列化类型

常用数据类型对应的hadoop数据序列化类型

java类型

HadoopWritable类型

boolean

BooleanWritable

byte

ByteWritable

int

IntWritable

float

FloatWritable

long

LongWritable

double

DoubleWritable

String

Text

Map

MapWritable

Array

ArrayWritable

6.自定义bean对象实现序列化接口(Writable)

自定义bean对象想要序列化传输,必须实现序列化接口

    1.必须实现Writable接口

    2.反序列化,需要反射调用空构造函数,所以必须有空构造函数

public FlowBean(){
    super();
}

    3.重写序列化方法

public void write(DataOutput out) throws IOException{
    out.writeLong(upFlow);
    out.writeLong(downFlow);
    out.writeLong(sumFlow);
}

    4.重写反序列化方法

public void readFields(DataInput in)throws IOException{
    upFlow=in.readLong();
    downFlow=in.readLong();
    SumFlow=in.readLong();
}

    5.注意反序列化的顺序和序列化的顺序要完全一致

    6.想要把结果显示在文件中,需要重写toString(),可用“\t”分开,方便后续用。

    7.如果需要将自定义的bean放在key中传输,则还需要实现comparable接口,应为mapreduce框架中的shuffle过程一定会对key进行排序。

public int compareTo(FlowBean o){
    return this.sumFlow > o.getSumFlow() ? -1:1;
}

七、MapReduce简单实用,实现WordCount

配置好hadoop环境和hadoop的jar包!!!!不会的看https://blog.csdn.net/qq_34886352/article/details/82220230

码字的时候也不要引错包了(特别是Text,Configuration这两个类),都是hadoop下的包

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

1.准备数据:新建一个txt文件,每行多个单词用空格隔开

2.程序分析

Mapper阶段

Reducer阶段

Driver阶段

1.将Maptask传给我们的文本内容转换成String

1.汇总各个key的个数

1.获取配置信息,或者job对象实例

2.根据空格将这一行切分成单词

2.输出该key的总次数

2.指定本程序的jar包所在的本地路径

3.将单词输出为<单词,1>

3.关联mapper/Reducer业务类

4.指定mapper输出数据的KV对类型

5.指定最终输出的数据的KV对类型

6.指定job的输入原始文件所在目录

7.提交

3.编写mapper代码

public class WordcountMapper extends Mapper<LongWritable,Text,Text,IntWritable> {
    /**
    * 输出的k
    */
    private Text k = new Text();
    /**
    * 输出的v
    */
    private IntWritable v =new IntWritable();

    /**
    * 重写map方法
    * 每对KV都会被执行一次map
    */
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        //将获取的一行数据转为String
        String line = value.toString();
        //通过空格切分数据
        String[] words = line.split(" ");
        //将数据处理成下一阶段需要使用KV对
        for (String word : words) {
            k.set(word);
            v.set(1);
            context.write(k,v);
        }
    }
}

4.编写reducer代码

/**
* Text 输入的key 单词
* IntWritable 输入的Value 每个单词的个数
* Text 输出的key 单词
* IntWritable 输出的Value 每个单词的个数
*
* @date 2018-09-05 14:25
* @since 1.0.0
*/
public class WordcountReducer extends Reducer<Text, IntWritable, Text, IntWritable> {

    /**
    * 重写reduce方法
    * ReduceTask进程对每一组相同K的KV对调用一次reduce()方法
    * 例如:从mapper传来的数据如下
    * hello 1
    * hello 1
    * world 1
    * world 1
    * 这时候会把key为hello的数据做成一组,value便是1,1
    */
    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
    int sum = 0;
        for (IntWritable value : values) {
            sum += value.get();
        }
        context.write(key, new IntWritable(sum));
    }
}

5.编写driver代码

public class WordcountDriver {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
    //获取job信息
    Configuration configuration = new Configuration();
    Job job = Job.getInstance(configuration);
    //获取jar包位置
    job.setJarByClass(WordcountDriver.class);
    //关联自定义的mapper和reducer
    job.setMapperClass(WordcountMapper.class);
    job.setReducerClass(WordcountReducer.class);
    //设置map输出数据类型
    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 result = job.waitForCompletion(true);
    //0是成功,1是失败
    System.exit(result?0:1);
    }
}

6.通过jar的方式运行(提交到服务器运行)

  1. 将程序打包成jar。包名随意(这里我交租wc.jar)

  2. 上传到hadoop所在服务器上。(这里我上传到/usr/hadoop下面)

  3. 在HDFS系统中上传数据(这里我上传到/usr下面)

  4. 执行代码hadoop jar 上传jar的路径 wordcountDriver的包路径 需要处理的文件路径 处理后数据存放的路径(hadoop jar /usr/hadoop/wc.jar com.gyx.mapreduce.WordcountDriver /user /user/data)

  5. 查看结果 hadoop fs -cat /usr/data/p*

7.在本地模式下运行(开发的时候运行)

  1. 设置好启动参数(第一个为需要处理的文件,第二个为输出路径文件夹不能存在,中间用空格隔开),运行就可以了

猜你喜欢

转载自blog.csdn.net/qq_34886352/article/details/82426534