MapReduce Shuffle过程深入理解

来源:互联网 发布:淘宝上差评多久消失 编辑:程序博客网 时间:2024/06/05 00:23

MapReduce执行过程

1、设置input,告诉程序输入的数据在那儿。

通过InputFormat接口子类(FileInputFormat, TextInputFormat),
(1)读取数据
(2)将数据转换成key-value形式交给Mapper的map()方法进行处理
默认key=行偏移量(LongWritable),value=行数据(Text)

//设置inputPath inpath = new Path(arg0[0]);FileInputFormat.addInputPath(job, inpath);

2、执行Mapper的map()方法

多个Mapper并行执行Mapper的map(KEYIN,VALUEIN,KEYOUT,VALUEOUT)方法。

3、shuffle

1、数据溢写,分区,排序
多个map并行工作相互不影响,每个map工作结束后会将输出的key-value对先存储在内存(memory)缓存区中,当内存缓冲区中数据的数量达到一定量后会将内存缓冲区中的数据溢写(spill)到本地磁盘中,此时可能会将内存缓冲中的数据写到多个磁盘文件中,再将数据写到磁盘文件的过程中,会对数据进行以下操作:
(1)分区:因为shuffle过程结束后,在reduce过程中,多个reduce会拉取map输出的数据并执行reduce操作,此时一个reduce会对应一个分区,即,不同的reduce处理不同分区的数据。所以此处的分区可以理解为:通过分区后,输出有多个文件,但是不同文件中存储的数据都是够被同一个reduce处理的数据,或者同一文件中的数据按照不同分区进行分开存放。
(2)将各个分区(文件)中的数据按照key进行排序。

2、文件合并
当map输出的数据全部写到磁盘后,会合并小文件(merge),将上一过程中输出的多个小文件进行合并,在合并的过程中也会进行分区和排序,同之前一样,分区即是把能被同一个reduce操作的数据放在一起,排序即是在各个分区中将数据按照key进行排序。此时通过合并,分区,排序后生成的大文件存放在map程序本机的本地磁盘,并等待reduce来拷贝数据。

3、Combiner和压缩
当执行到此处,可以进行以下两个可设置的操作:
(1)Combiner(map端的reduce),文件合并的时候,可以执行reduce端操作(将相同key的value合并在一起),这种操作不是每个reduce程序都可以进行,只有不影响最终结果的情况下,才能够进行Combiner操作。
(2)压缩:在此处可以将合并后的大文件进行压缩,压缩后可以减少磁盘的占用量,当reduce copy时也可以减少网络IO。

4、reduce数据copy
接下来reduce task会到map task运行的主机上拷贝自己要处理的数据。具体的过程如下
(1)先将数据放内存缓冲区,缓冲区写满后溢写到磁盘。
(2)溢写磁盘的过程中,会将拷贝的数据进行合并,排序处理。
(3)group:在数据copy结束并合并结束后,将拷贝的数据进行按照key进行分组,即,将相同key的value放在一起,这里通过比较器进行比较key是否相同。
(4)将分组后的数据传递给reduce()方法进行处理
这里写图片描述

4、执行Reducer的reduce()方法

执行reduce(KEYIN, VALUEIN, KEYOUT,VALUEOUT)方法

5、设置output,执行后的结果存放在那儿

通过接口OutputFormat的子类(FileOutputFormat,TextOutputFormat等)默认每个key-value输出一行,key和value中间的分隔符为制表符,key,value输出是调用key和value的toString()方法。
//设置outputPath outpath = new Path(arg0[1]);FileOutputFormat.setOutputPath(job, outpath);

Shuffle过程中的设置

    public  int run(String[] arg0) throws Exception{//        Configuration configuration = new Configuration();        //读取配置文件信息        Configuration conf = getConf();//        conf.set("mapreduce.map.output.compress", "true");//设置开启压缩,默认为false//        conf.set("mapreduce.map.output.compress.codec", "org.apache.hadoop.io.compress.Lz4Codec");//设置压缩算法,CompressionCodec为压缩算法的父类//        CompressionCodec        //创建Job        Job job = Job.getInstance(conf, this.getClass().getSimpleName());        //设置运行的jar        job.setJarByClass(this.getClass());        //设置input        Path inpath = new Path(arg0[0]);        FileInputFormat.addInputPath(job, inpath);        //设置map        //TODO    需要修改ModelMapper        job.setMapperClass(ModelMapper.class);        //TODO    需要修改map的输出类型        job.setMapOutputKeyClass(Text.class);        job.setMapOutputValueClass(IntWritable.class);//*******************shuffle************************//        job.setPartitionerClass(cls);     //设置分区//        job.setSortComparatorClass(cls);    //设置排序//        job.setCombinerClass(cls);          //设置combiner//        job.setGroupingComparatorClass(cls);    //设置group//*******************shuffle************************        //设置reduce        //TODO    需要修改ModelReduce        job.setReducerClass(ModelReduce.class);        //TODO    需要修改reduce的输出类型        job.setOutputKeyClass(Text.class);        job.setOutputValueClass(IntWritable.class);        //设置output        Path outpath = new Path(arg0[1]);        FileOutputFormat.setOutputPath(job, outpath);        //提交job,设置为true会在运行的时候打印日志信息        boolean isSuccess = job.waitForCompletion(true);        return isSuccess ? 0 : 1;    }