Loading

MapReduce

1 介绍

​ MapReduce是一个软件框架,基于该框架能够容易地编写应用程序,这些应用程序能够运行在由上千个商用机器组成的大集群上,并以一种可靠的,具有容错能力的方式并行地处理上TB级别的海量数据集。

  • 思想:分而治之

  • Mapper负责“分”:

    • 即把复杂的任务分解为若干个“简单的任务”来处理
      • 一是数据或计算的规模相对原任务要大大缩小
      • 二是就近计算原则,即任务会分配到存放着所需数据的节点上进行计算(移动数据不如移动计算)
      • 三是这些小任务可以并行计算,彼此间几乎没有依赖关系
  • Reducer负责对map阶段的结果进行汇总

    • Reducer个数,可通过在mapred-site.xml配置文件里设置参数mapred.reduce.tasks的值,缺省值为1
  • MapReduce工作机制

    # 1.客户端,用来提交MapReduce作业
    # 2.JobTracker,用来协调作业的运行
    # 3.TaskTracker,用来处理作业划分后的任务
    # 4.HDFS,用来在其它实体间共享作业文件
    

2 架构

  • 执行流程:

    • 一个MapReduce作业通常会把输入的数据集切分为若干独立的数据块,由Map任务以完全并行的方式去处理它们
    • 框架会对Map的输出先进行排序,然后把结果输入给Reduce任务。中间结果会存储在hdfs中,框架负责任务的调度和监控,以及重新执行已经关闭的任务
    • MapReduce框架和分布式文件系统是运行在一组相同的节点上,也就是说,计算节点和存储节点通常都是在一起的。可以使得整个集群的网络带宽被非常高效地利用
  • 框架组成

    • JobTracker:

      负责调度构成一个作业的所有任务,这些任务分布在不同的TaskTracker上

    • TaskTracker

      负责执行由JobTracker指派的任务

  • 输入输出

    MapReduce框架运转在<key,value>键值对上,输入输出的键值可能不同。

    Client:<k1,v1> --> Map: <k2,v2>  --> combine:<k2,v2>  --> Reudce:<k3,v3>  --> HDFS
    
  • 代码演示

package wc1;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;

import java.io.IOException;
import java.util.StringTokenizer;

public class wordcount {
    /**
     * @author codingyin
     * <p>
     * Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>
     * KEY IN : 行的起始偏移量,原生类型long,对应的Hadoop序列化类型:LongWritable
     * VALUE IN:行的文本内容,原生类型String,对应的Hadoop序列化类型:Text
     * KEY OUT: 一个单词,原生类型String,对应的Hadoop序列化类型:Text
     * VALUE OUT: 单词次数1,原生类型int,对应的Hadoop序列化类型:IntWritable
     * String ,Long都是jdk中自带的数据类型,在序列化的时候,效率比较低。hadoop为了提高序列化的效率,他就自己自定义了一套数据结构
     * Long------->LongWritable
     * String----->Text
     * Integer---->IntWritable
     * null------->nullWritable
     */
    public static class MyMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
        private final static IntWritable one = new IntWritable(1);
        private Text word = new Text();

        /**
         * maptask程序在自己的任务范围数据中(数据切片)
         * 每读取一行数据,就调一次本map()方法,并将读到的数据以key-value形式作为参数传给本map()方法
         * map()方法按照所需的数据处理逻辑对传入的数据进行处理,然后通过context返回给map task程序
         */
        @Override
        public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
            StringTokenizer itr = new StringTokenizer(value.toString());
            while (itr.hasMoreTokens()) {
                word.set(itr.nextToken());
                context.write(word, one);
            }
        }
    }

    /***
     * @author codingyin
     *         Reduce Task会将自己所收到的key-value数据按照key进行排序和分组,相同key的数据会连续排放在一起,key“较小”的数据排在前面,然后,将key相同的key-value数据作为一组来进行聚合处理;
     *         Reduce Task对每一组数据处理时,都是调用用户提供的reduce方法,并传入一个key对象和一个迭代器对象给reduce方法来获取这一组数据中的每一个key-value数据;
     *         在reduce函数中,我们只需要使用框架所提供的迭代器来迭代reduce task提供的一组key-value数据(具体来说,就是循环反复调用迭代器的next()方法来逐一获取这组数据中的每一对key-value数据);
     *         将每次迭代到的value值进行累加,迭代完成之后,累加值即为某一个key(即某个单词)的总词频;
     *         将统计出的这个单词及其总词频,通过reduce task所提供的context进行返回。
     */

    public static class Myreduce extends Reducer<Text, IntWritable, Text, IntWritable> {

        /**
         * Reduce Task收集到一组相同key的数据,调一次本方法,比如(a,1)(a,1)(a,1)......(a,1)
         */
        @Override
        public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
            int count = 0;
            for (IntWritable val : values) {
                count += val.get();
            }
            context.write(key, new IntWritable(count));
        }
    }

    /**
     * @author codingyin
     * 入口函数类并且对配置文件进行一些必要的设置
     */

    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf, "word count");
        job.setJarByClass(wordcount.class);

        // 指定本次job所要用的mapper类和reducer类
        job.setMapperClass(MyMapper.class);
        job.setCombinerClass(Myreduce.class);
        job.setReducerClass(Myreduce.class);

        // 指定本次job的mapper类和reducer类输出结果的数据类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(LongWritable.class);

        // 指定本次job读取源数据时所需要用的输入组件:源文件在hdfs的文本文件中,用TextInputFormat
        job.setInputFormatClass(TextInputFormat.class);
        // 指定本次job输出数据时所需要用的输出组件:输出到hdfs文本文件中,用TextOutputFormat
        job.setOutputFormatClass(TextOutputFormat.class);

        // 指定reduce task运行实例的个数
        job.setNumReduceTasks(3);

        // 指定源数据文件所在的路径
        FileInputFormat.addInputPath(job, new Path(args[1]));
        // 指定输出结果数据文件所在的路径
        FileOutputFormat.setOutputPath(job, new Path(args[2]));
        // 核心代码: 提交jar包给yarn
        // job.submit();  // 提交完任务,客户端就退出了
        // 提交完任务,客户端打印集群中的运行进度信息,并等待最终运行状态
        System.exit(job.waitForCompletion(true) ? 0 : 1);

    }


}

posted @ 2022-01-10 21:39  偏执的感叹号  阅读(240)  评论(0)    收藏  举报