回顾——MapReduce计算模型

(上一节我们回顾了HDFS文件系统,以及其架构原理。那么在这一节,我们来介绍Hadoop的离线计算模型——MapReduce,并以wordcount程序为例详细讲解。)

MapReduce——Hadoop的并行计算框架,来源于谷歌,是一种基于HDFS的离线计算框架。在Hadoop2的YARN出现以后,MR便运行在YARN上了。与Hadoop相关的框架如Hive,Hbase,sqoop等其内部计算都是基于MapReduce。所以想学好hadoop,MapReduce是必不可少的一大重点。

MapReduce主要包含五大模块Input,Map,Shuffle,Reduce,Output,其中shuffle过程是MapReduce中最为复杂,也是灵活性最高的一部分。
我们首先以大数据界的“Hello World”——WordCount程序为例来介绍MapReduce。WordCount程序简单的说就是将一个文件中每一个单词出现的次数统计出来。因为是入门级程序,所以在WordCount中我们只涉及到Map和Reduce。

回顾——MapReduce计算模型_第1张图片

整个WordCount的MapReduce过程如上图(大写的大家默认是小写的吧,忘记修改了):
(1)首先从文件中以键值对的形式读取数据,默认键为行偏移量,值为一行的字符串;
(2)接着进入Map端。每一次在Map端读取的都是一行字符串,我们先将它按空格分隔,并循环逐一输出。输出的键值对为(字符串,1)(因为MapReduce的整个过程都是以键值对的形式进行的,所以在Map端的输入输出都是键值对);
(3)进入shuffle过程,这个过程包括分区、排序、分组等等过程;在这里我们只关注分组,shuffle过程默认把相同key的value值存入一个组中,然后将一个迭代器作为值传入reduce端;
(4)reduce收到shuffle过程传来的键值对为,key-字符串,value-迭代器;我们在reduce阶段循环将迭代器中的数字“1”累加,最后的到一个和,那么这个和就是最终的单词数;
(5)将单词和对应的单词数输出到文件中。

回顾——MapReduce计算模型_第2张图片

代码结构如下:

public class MyMapReduce extends Configured implements Tool {
    public static class MyMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
        Text outKey = new Text();
        IntWritable outValue = new IntWritable(1);
        @Override
        protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
            // TODO Auto-generated method stub
            String[] str = value.toString().split(" ");
            for (int i = 0; i < str.length; i++) {
                outKey.set(str[i]);
                context.write(outKey, outValue);
            }
        }
    }

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

        IntWritable outValue = new IntWritable();

        @Override
        protected void reduce(Text key, Iterable value,
                Context context) throws IOException, InterruptedException {
            // TODO Auto-generated method stub
            int sum = 0;
            for (IntWritable v : value) {
                sum += v.get();
            }
            outValue.set(sum);
            context.write(key, outValue);
        }
    }

    public int run(String[] args) throws Exception {
        // TODO Auto-generated method stub
        Job job = Job.getInstance(this.getConf());
        job.setJarByClass(this.getClass());

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

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

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

        boolean status = job.waitForCompletion(true);
        return status ? 1 : 0;
    }

    public static void main(String[] args) throws Exception {
        Configuration configuration = new Configuration();
        int result = ToolRunner.run(configuration, new MyMapReduce(), args);
        System.exit(result);
    }
}

(1)整个MapReduce类需要继承一个Configured父类,其中包括的是hdfs等等配置信息;
(2)实现一个Tool接口中的run方法,在这个方法中实例化一个Job,指定配置,jar包;而且还需要设置了Map和Reduce的输入输出类型,文件路径等等信息;最后提交job。
(3)在主函数中我们调用ToolRunner的run方法来执行我们的mapreduce程序,其中需要传入配置对象和MapReduce的类;
(4)MapReduce中的基本数据类型包括Intwritable,Text等等;Context是上下文信息,负责输出每个模块的键值。

如果把MapReduce细分,可以分为一下几大过程
Input-Split(输入分片):此过程是将从HDFS上读取的文件分片,然后送给Map端。有多少分片就有多少Mapper,一般分片的大小和HDFS中的块大小一致。
Shuffle-Spill(溢写):每个Map任务都有一个环形缓冲区,在默认情况下,缓冲区的大小为100MB,可以调整。一旦缓冲区达到阈值80%,一个后台线程便开始把内容“溢写”-“spill”到磁盘。在溢写过程中,map将继续输出到剩余的20%空间中,互不影响,如果缓冲区被填满map会被堵塞直到写磁盘完成。(在写磁盘之前会经历分区,排序,分组等过程)
Shuffle-Partition(分区):由于每个Map可能处理的数据量不同,所以到达reduce有可能会导致数据倾斜。分区可以帮助我们解决这一问题,在shuffle过程中会按照默认key的哈希码对分区数量取余,reduce便根据分区号来拉取对应的数据,达到数据均衡。分区数量对应Reduce个数。
Shuffle-Sort(排序):在分区后,会对此分区的数据进行内排序,排序过程会穿插在整个MapReduce中,在很多地方都存在。
Shuffle-Group(分组):分组过程会把key相同的value分配到一个组中,wordcount程序就利用了分组这一过程。
Shuffle-Combiner(组合):这一过程我们可以理解为一个小的Reduce阶段,当数据量大的时候可以在map过程中执行一次combine,这样就相当于在map阶段执行了一次reduce。由于reduce和map在不同的节点上运行,所以reduce需要远程拉取数据,combine就可以有效降低reduce拉取数据的量,减少网络负荷。(这一过程默认是不开启的,在如求平均值的mapreduce程序中不要使用combine,因为会影响结果。)
Compress(压缩):在缓冲区溢写磁盘的时候,可以对数据进行压缩,节约磁盘空间,同样减少给reducer传递的数据量。
Reduce-Merge(合并):reduce端会拉取各个map输出结果对应的分区文件,这样reduce端就会有很多文件,所以在此阶段,reduce再次阶段将它们合并,排序再送入reduce执行。
Output(输出):在reduce阶段,对已排序输出中的每个键调用reduce函数。此阶段的输出直接写到输出文件系统,一般为HDFS。

回顾——MapReduce计算模型_第3张图片

(MapReduce是Hadoop的计算框架,在Hadoop的各个框架中的计算都使用MapReduce。虽然其编程模型简单,但是针对某些问题的MapReduce程序还是比较难写的。下一节我们使用MapReduce来编写二次排序算法,其中会涉及到自定义数据类型,重写shuffle过程等等。)

你可能感兴趣的:(hadoop,大数据)