MapReduce详解

说明:这是我的第一篇博客,主要是自己经常在上面看博客,看了又忘,所以现在尝试着写写,加强自己的理解,里面很多都是前辈博客主的东西,我只是总结总结,版权不是我所有哈~~下面开始吧

一、内容总体说明

  看了很多文章,感觉大家都是说的某一个具体的方面,现在我把这些博客综合下,让大家对mapreduce有一个深入而全面的了解,其中很多思想都来源于这个网址的作者,在此表示深深的感谢,写的真的是太好了,让我受益匪浅。http://www.cnblogs.com/sharpxiajun/p/3151395.html(还有很多作者就不一一列举了)

二、wordcount程序详解

package com.felix;
import java.io.IOException;
import java.util.Iterator;
import java.util.StringTokenizer;
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.mapred.FileInputFormat;
import org.apache.hadoop.mapred.FileOutputFormat;
import org.apache.hadoop.mapred.JobClient;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapred.MapReduceBase;
import org.apache.hadoop.mapred.Mapper;
import org.apache.hadoop.mapred.OutputCollector;
import org.apache.hadoop.mapred.Reducer;
import org.apache.hadoop.mapred.Reporter;
import org.apache.hadoop.mapred.TextInputFormat;
import org.apache.hadoop.mapred.TextOutputFormat;
/**
 * 
 * 描述:WordCount explains by Felix
 * @author Hadoop Dev Group
 */
public class WordCount
{
    /**
     * MapReduceBase类:实现了Mapper和Reducer接口的基类(其中的方法只是实现接口,而未作任何事情)
     * Mapper接口:
     * WritableComparable接口:实现WritableComparable的类可以相互比较。所有被用作key的类应该实现此接口。
     * Reporter 则可用于报告整个应用的运行进度,本例中未使用。 
     * 
     */
    public static class Map extends MapReduceBase implements
            Mapper
   
   
    
    
    {
        /**
         * LongWritable, IntWritable, Text 均是 Hadoop 中实现的用于封装 Java 数据类型的类,这些类实现了WritableComparable接口,
         * 都能够被串行化从而便于在分布式环境中进行数据交换,你可以将它们分别视为long,int,String 的替代品。
         */
        private final static IntWritable one = new IntWritable(1);
        private Text word = new Text();
        
        /**
         * Mapper接口中的map方法:
         * void map(K1 key, V1 value, OutputCollector
    
    
     
      output, Reporter reporter)
         * 映射一个单个的输入k/v对到一个中间的k/v对
         * 输出对不需要和输入对是相同的类型,输入对可以映射到0个或多个输出对。
         * OutputCollector接口:收集Mapper和Reducer输出的
     
     
      
      对。
         * OutputCollector接口的collect(k, v)方法:增加一个(k,v)对到output
         */
        public void map(LongWritable key, Text value,
                OutputCollector
      
      
       
        output, Reporter reporter)
                throws IOException{
            String line = value.toString();
            StringTokenizer tokenizer = new StringTokenizer(line);
            while (tokenizer.hasMoreTokens()){
                //记录:word名,和数值1
                word.set(tokenizer.nextToken());
                output.collect(word, one);
            }
        }
    }
    public static class Reduce extends MapReduceBase implements
            Reducer
       
       
         { public void reduce(Text key, Iterator 
        
          values, OutputCollector 
         
           output, Reporter reporter) throws IOException{ int sum = 0; //累加每个word值 while (values.hasNext()){ sum += values.next().get(); } output.collect(key, new IntWritable(sum)); } } public static void main(String[] args) throws Exception { /** * JobConf:map/reduce的job配置类,向hadoop框架描述map-reduce执行的工作 * 构造方法:JobConf()、JobConf(Class exampleClass)、JobConf(Configuration conf)等 */ JobConf conf = new JobConf(WordCount.class); conf.setJobName("wordcount"); //设置一个用户定义的job名称 conf.setOutputKeyClass(Text.class); //为job的输出数据设置Key类 conf.setOutputValueClass(IntWritable.class); //为job输出设置value类 conf.setMapperClass(Map.class); //为job设置Mapper类 conf.setCombinerClass(Reduce.class); //为job设置Combiner类 conf.setReducerClass(Reduce.class); //为job设置Reduce类 conf.setInputFormat(TextInputFormat.class); //为map-reduce任务设置InputFormat实现类 conf.setOutputFormat(TextOutputFormat.class); //为map-reduce任务设置OutputFormat实现类 /** * InputFormat描述map-reduce中对job的输入定义 * setInputPaths():为map-reduce job设置路径数组作为输入列表 * setInputPath():为map-reduce job设置路径数组作为输出列表 */ FileInputFormat.setInputPaths(conf, new Path(args[0])); FileOutputFormat.setOutputPath(conf, new Path(args[1])); JobClient.runJob(conf); //运行一个job } } 
          
         
       
      
      
     
     
    
    
   
   


代码片段已经给出了非常详细的注释,可以看到各个语句的意义,可以上图非常清晰的给出了wordcount程序的流程

  1. 先对输入的数据进行分片
  2. 对每一个split进行map统计
  3. 对所有的map结果进行合并排序
  4. 对排序后的结果进行reduce累加求和
  5. 把结果写入到输出文件名中

三、从运行流程的角度分析mapreduce


         第一步:jobclient部分提交代码---->jobTracker

                          会直接调用jobClient中的submitJob方法,接下来主要做下面几件事:

1.向jobTracker请求一个jobId;

2.jobTracker会检查outputpath是否是已经存在,如果存在的话,就给client端抛异常;

3.开始计算输入分片,如果无法计算,比如输入路径是错的,找不到文件,向client端抛异常

4.将job需要的资源,如jar文件,配置文件,还有分片信息,写入到一个以jobId为文件名的路径下,这个地方往往会有多个副本让taskTracker进行访问

         第二步:jobTracker的调度分配过程

jobTracker会把上面的形成一个job,以jobId进行标识,放入到一个队列中,等待该job的运行。等待执行该任务的时候,会形成一个监视对象,保存该job的相关信息,并为每一个split开辟一个map,并读取配置文件,计算出reduce数

 第三步:taskTracker的map过程

1.taskTracker会通过心跳去跟jobTracker进行通信,通知是否完成map等等

2.taskTracker会创建一个专门的目录,会把jar复制到该,从而能够去执行map过程

3.根据当前分片大小和起始地址,开始map计算

4.完成后,通过心跳通知jobTracker的某个reduce来复制map输出结果

 第四步:reduce阶段

1.当最后一个map完成后,jobTracker会通知有reduce任务的taskTracker到指定的地点(一般是多个map-Task位置)去复制结果

2.由于这上面涉及到跨节点传输等问题,所以上面的每个map结果都会进行局部排序合并压缩等操作

3.结果复制完后,执行reduce

4.jobTracker合并所有的reduce结果

第五步:结果的合并和路径清除

把最终合并后的reduce结果,写入到输出路径下,一般很多都写入到了数据库,并删除之前用到的临时存储路径


四、从逻辑计算的角度分析mapreduce

第一步:split阶段

1.在进行map计算之前,mapreduce会根据输入文件计算输入分片(input split),每个输入分片(input split)针对一个map任务,输入分片(input split)存储的并非数据本身,而是一个分片长度和一个记录数据的位置的数组

2.输入分片(input split)往往和hdfs的block(块)关系很密切,可以从配置文件读取split单位,只会分小片,不会跨节点把多个hdfs的block分给一个split,比如是64mb,如果我们输入有三个文件,大小分别是3mb、65mb和127mb,那么mapreduce会把3mb文件分为一个输入分片(input split),65mb则是两个输入分片(input split)而127mb也是两个输入分片(input split)

第二步:map阶段

根据copy过来的jar中的map过程进行map计算

第三步:单个taskTracker的combine过程

combiner阶段是程序员可以选择的,combiner是一种本地化的reduce操作,它是map运算的后续操作,主要是在map计算出中间文件前做一个简单的合并重复key值的操作,主要的目的还是提高跨节点传输的时候,提高传输效率。例如我们对文件里的单词频率做统计,map计算时候如果碰到一个hadoop的单词就会记录为1,但是这篇文章里hadoop可能会出现n多次,那么map输出文件冗余就会很多,因此在reduce计算前对相同的key做一个合并操作,那么文件会变小,这样就提高了宽带的传输效率,毕竟hadoop计算力宽带资源往往是计算的瓶颈也是最为宝贵的资源,但是combiner操作是有风险的,使用它的原则是combiner的输入不会影响到reduce计算的最终输入,例如:如果计算只是求总数,最大值,最小值可以使用combiner,但是做平均值计算使用combiner的话,最终的reduce计算结果就会出错。

第四步:shuffle阶段

1.跟map相关的部分:将map的输出作为reduce的输入的过程就是shuffle了,这个是mapreduce优化的重点地方。Shuffle一开始就是map阶段做输出操作,一般mapreduce计算的都是海量数据,map输出时候不可能把所有文件都放到内存操作,因此map写入磁盘的过程十分的复杂,更何况map输出时候要对结果进行排序,内存开销是很大的,map在做输出时候会在内存里开启一个环形内存缓冲区,这个缓冲区专门用来输出的,如果缓存区被撑满了,那么map就会阻塞写入内存的操作,让写入磁盘操作完成后再继续执行写入把map结果写入内存(写入磁盘前会有个排序操作,做这个排序前还会执行combiner操作),每次写入磁盘操作时候就会写一个溢出文件,一般会有多个溢出文件。等map输出全部做完后,map会合并这些输出文件。

2.与reduce相关的部分:会启动多个复制过程,去复制这些合并后的结果(默认5个线程),复制时候reduce还会进行排序操作和合并文件操作,这些操作完了就会进行reduce计算了。

第五步:reduce阶段:和map函数一样也是程序员编写的,最终结果是存储在hdfs上的。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值