Hadoop关于MapReduce

来源:互联网 发布:无人深空优化补丁3dm 编辑:程序博客网 时间:2024/05/22 11:32

简述

MapReduce是一种可用于数据处理的编程模型,用以进行大数据量的计算。Hadoop可以运行各种上语言版本的MapReduce程序。

MapReduce程序本质上是并行运行的,因此可以将大规模的数据分析任务分发给任何一个拥有足够多机器的数据中心。

MapReduce的优势在于处理大规模数据集。

为了加快处理速度,我们需要并行处理程序来进行数据分析。从理论上讲,我们可以使用计算机上所有可有的硬件线程来处理,每个线程负责处理不同的任务,但是仍然存在一些问题。

在hadoop中,一个MapReduce作业通常会把输入的数据集切分为若干独立的数据块,由map任务以完全并行的方式去处理它们。框架会对map的输出行进行排序,然后把结果输入给reduce任务。整个框架负责任务的调度和监控,以及重新执行已经关闭的任务。


Map和Reduce

MapReduce任务过程分为两个处理阶段:map阶段和reduce阶段。每个阶段都以键值对作为输入输出,其类型由程序员来选择。程序员还需要写两个函数:map函数和reduce函数。

map阶段的是原始数据。我们选择文本格式作为输入格式,将数据集的每一行作为文本输入。键是某一行起始位置相对于文件起始位置的偏移量,值是需要处理的内容。


简单示例:

一个简单的文本,通过MapReduce统计单词个数

Hello Edison ChouHello Hadoop RPCHello Wncud ChouHello Hadoop MapReduceHello Dick Gu

Map函数:

 /**     * @author Edison Chou     * @version 1.0     * @param KEYIN     *            →k1 表示每一行的起始位置(偏移量offset)     * @param VALUEIN     *            →v1 表示每一行的文本内容     * @param KEYOUT     *            →k2 表示每一行中的每个单词     * @param VALUEOUT     *            →v2 表示每一行中的每个单词的出现次数,固定值为1     */    public static class MyMapper extends            Mapper<LongWritable, Text, Text, LongWritable> {        protected void map(LongWritable key, Text value,                Mapper<LongWritable, Text, Text, LongWritable>.Context context)                throws java.io.IOException, InterruptedException {            String[] spilted = value.toString().split(" ");            for (String word : spilted) {                context.write(new Text(word), new LongWritable(1L));            }        };    }

这个Mapper类是一个泛型类型,它有四个形参类型,分别指定map函数的输入键、输入值、输出键和输出值的类型。

map方法这里有三个参数,前面两个key, value就是输入的key和value,第三个参数Context context用于输出内容的定写入。


Reduce函数:

 /**     * @author Edison Chou     * @version 1.0     * @param KEYIN     *            →k2 表示每一行中的每个单词     * @param VALUEIN     *            →v2 表示每一行中的每个单词的出现次数,固定值为1     * @param KEYOUT     *            →k3 表示每一行中的每个单词     * @param VALUEOUT     *            →v3 表示每一行中的每个单词的出现次数之和     */    public static class MyReducer extends            Reducer<Text, LongWritable, Text, LongWritable> {        protected void reduce(Text key,                java.lang.Iterable<LongWritable> values,                Reducer<Text, LongWritable, Text, LongWritable>.Context context)                throws java.io.IOException, InterruptedException {            long count = 0L;            for (LongWritable value : values) {                count += value.get();            }            context.write(key, new LongWritable(count));        };    }
Reduce类也是有四个形参类型用于指定输入和输出类型,reduce函数的输入类型必须匹配map函数的输出类型:即Text类型和Intwritable类型。

Reduce函数的输入也是一个key/value的形式,不过它的value是一个迭代器的形式Iterable<IntWritable> values,也就是说reduce的输入是一个key对应一组的值的value,reduce也有context和map的context作用一致。


Main函数的调用:

//输入文件
public static final String INPUT_PATH = "hdfs://hadoop-master:9000/testdir/input/words.txt";
//输出目录
public static final String OUTPUT_PATH = "hdfs://hadoop-master:9000/testdir/output/wordcount";

public static void main(String[] args) throws Exception {        Configuration conf = new Configuration();        // 0.0:首先删除输出路径的已有生成文件        FileSystem fs = FileSystem.get(new URI(INPUT_PATH), conf);        Path outPath = new Path(OUTPUT_PATH);        if (fs.exists(outPath)) {            fs.delete(outPath, true);        }        Job job = new Job(conf, "WordCount");        job.setJarByClass(MyWordCountJob.class);        // 1.0:指定输入目录        FileInputFormat.setInputPaths(job, new Path(INPUT_PATH));        // 1.1:指定对输入数据进行格式化处理的类(可以省略)        job.setInputFormatClass(TextInputFormat.class);        // 1.2:指定自定义的Mapper类        job.setMapperClass(MyMapper.class);        // 1.3:指定map输出的<K,V>类型(如果<k3,v3>的类型与<k2,v2>的类型一致则可以省略)        job.setMapOutputKeyClass(Text.class);        job.setMapOutputValueClass(LongWritable.class);        // 1.4:分区(可以省略)        job.setPartitionerClass(HashPartitioner.class);        // 1.5:设置要运行的Reducer的数量(可以省略)        job.setNumReduceTasks(1);        // 1.6:指定自定义的Reducer类        job.setReducerClass(MyReducer.class);        // 1.7:指定reduce输出的<K,V>类型        job.setOutputKeyClass(Text.class);        job.setOutputValueClass(LongWritable.class);        // 1.8:指定输出目录        FileOutputFormat.setOutputPath(job, new Path(OUTPUT_PATH));        // 1.9:指定对输出数据进行格式化处理的类(可以省略)        job.setOutputFormatClass(TextOutputFormat.class);        // 2.0:提交作业        boolean success = job.waitForCompletion(true);        if (success) {            System.out.println("Success");            System.exit(0);        } else {            System.out.println("Failed");            System.exit(1);        }    }



MapReduce作业(job)是客户端需要执行的一个工作单元:它包括输入数据、MapReduce程序和配置信息。Hadoop将作业分成若干个小任务(task)来执行,其中包括两类任务:map任务和reduce任务。

有两类节点控制着作业执行过程:一个jobtracker及一系列tasktracker。jobtracker通过高度tasktracker上运行的任务来协调所有运行在系统上的作业。tasktracker在运行任务的同时将运行进度报告给jobtracker,jobtracker由此记录每项作业任务的整体进度。如果其中一个任务失败,jobtracker可以在另外一个tasktracker节点上重新调试该任务。


一个reduce任务图。虚线框表示节点,虚线箭头表示节点内部的数据传输,实线箭头表示不同节点之间的数据传输。



如果有多个reduce任务,每个map任务将会针对输出进行分区,即每个reduce任务建一个分区。分区可由用户定义分区函数控制,通常默认的分区通过哈希函数来分区。

map的个数是看文件的大小,分片之后自动启动多少个map。reduce启动多少个是看实际业务,默认是一个。

setNumberReduceTesks();进行设置多个reduce。

以上简单示例场景不适合多map多reduce,只适合一个reduce。

hadoop先前版本block默认64M,现在hadoop2默认128M。mapreduce输入数据的分割大小跟hdfs存储块大小是一样的。




无reduce任务的MapReduce数据流:



mapreduce运行机制执行顺序:输入分片(input split)、map阶段、combiner阶段、shuffle阶段和reduce阶段。

1、输入分片:在进行map计算之前,mapreduce会根据输入文件计算分片(跟hdfs分区默认大小一样),每个输入分片针对一个map任务,分片存储的并非数据本身,而是一个分片长度和一个记录数据的位置的数组。


2、map阶段:就是程序员编写好的map函数,一般map操作都是本地化操作也就是在数据存储节点上进行的。


3、combiner阶段:combiner阶段是可选择的,combiner其实也是一种reduce操作,combiner是一个本地化的reduce操作,它是map运算的后续操作,主要是在map计算出中间文件前做一个简单的合并重复key值的操作,这样文件会变小,可以提高宽带的传输效率。


4、shuffle阶段:将map的输出作为reduce的输入的过程就是shuffle。一般map输出的时候不可能把所有文件都放到内存操作,因此map在做输出的时候会在内存里开户一个环形内存缓冲区专门用来做输出的,默认是100M,并且在配置文件里设定阀值,如果缓冲区达到了阀值的80%,这个时候就会把内容写到磁盘上,这个过程叫spill,另外的20%内存可以继续写入要写进磁盘的数据,写入磁盘和写入内存操作互不干扰,如果缓存区满了,那么map就会阻塞写入内存的操作,让写入磁盘操作完成后再执行写入内存操作。写入磁盘前会有个排序操作,这个是在写入磁盘的时候进行的,如果定义了combiner函数,那么排序前会执行combiner操作。


5、reduce阶段:和map函数一样,是需要程序员编写,指定最终存储位置。