MapReduce过程详细解析和使用

来源:互联网 发布:android studio编程 编辑:程序博客网 时间:2024/05/23 23:12

本文为博主原创,允许转载,但请声明原文地址:http://www.coselding.cn/article/2016/05/31/MapReduce过程详细解析和使用/

    MapReduce的详细执行过程和MapReduce程序编程过程中可能会用的东西,参考了很多大神的博客,后来发现好好钻研Hadoop源码会对过程的理解帮助很大,这个文章主要涉及MapReduce,由于Hadoop2.x对之前版本的兼容性设计,因此适用于Hadoop1.x和Hadoop2.x,如有错误,还望指出哦~
    好好理解MapReduce的整个过程以及每个步骤对过程的影响,这样才有利于实际开发中应对实际业务需求能够设计出高效优雅的MR程序~

    看这篇博客的话要在我前面的文章学会了才行哦,不然你会看的很懵逼,因为这篇文章是从编程角度对MapReduce过程的重新说明,涉及到的内容都是为实际MapReduce算法设计服务的~

参考博客:
    MapReduce详细过程:http://blog.sina.com.cn/s/blog_8fb3261b0101arvk.html
    MapReduce的雅虎文章翻译(这个大神的Hadoop一系列文章都不错哦,说得很详细,赞一个):http://www.cnblogs.com/spork/archive/2010/01/11/1644342.html
    MapReduce输入过程源码分析:http://www.cnblogs.com/shitouer/archive/2013/02/28/hadoop-source-code-analyse-mapreduce-inputformat.html

job.setUser(user):设置当前作业的执行用户。

Map阶段:
    1、 输入文件解析(自定义分块和键值对解析):
        InputSplit:MapReduce先根据输入路径扫描输入文件,并将输入文件列表分割成一个个的InputSplit;
        RecordReader:记录读取器,每次初始化调用处理一个InputSplit分块,RecordReader的其他方法用来自定义如何对这个分片提取K-V对,并输出个Map作为Map的输入;
如何使用:
       (1)MapReduce的JobRun类有setInputFormatClass方法,需要自己写一个InputFormat类的子类,在getSplits方法中通过上下文对象和HDFS的JavaAPI接口获取输入文件,并按照自己的需求进行分块,放到List<InputSplit>(具体方法类似Java的File的定位字节读取来分块);
       (2)这样我们就完成了输入文件分块了对吧,MapReduce也能收到这些分块了,这时InputFormat的另一个方法createRecordReader,在这里返回我们自定义的RecordReader,MapReduce就能自动调用我们传入的这个对象对每个分块进行我们自定义的K-V的提取工作。
          默认:这个步骤可以都不做,默认情况下的MapReduce会根据配置文件中设置的分块大小对文件进行分块,并按行进行键值对解析(Key为改行在文件中的偏移量,Value为改行值)。
    2、 Map:对刚才从文件中提取的K-V对进行用户需要的提取处理,并生成Map输出的新的K-V对,作为Reduce的输入;
       使用:job.setMapperClass方法
    3、 Merge:Map的输出是先输出到内存中的,内存缓冲区满了就溢写到文件,因此多次溢写将会产生多个溢写文件,Merge就是把这些溢写文件合并成一个文件;
    4、 Partitioner:Map之后,自定义Map的输出的分区方式,这个分区将影响K-V是输出到哪个Reduce去处理的,同一分区送到同一个reducer进行处理
       使用:job.setPartitionerClass方法
       注意:
          (1) 这里的分区是否合理将会影响到Reduce操作是否产生数据倾斜问题,设计一个合理的分区算法尤为重要;
          (2) 有时根据业务和算法的需要可以自定义成方便后续Reduce处理的辅助操作;
       默认根据Key的hashCode进行分区(HashPartitioner,该算法在某些情况下还是会产生数据倾斜问题,不同的业务需求有必要设计各自的分区算法)
    5、 SortComparator:是一个WritableComparator类的子类,在里面自定义排序的比较器,MapReduce在排序的时候通过这个比较器进行排序,排序控制同一分区中的数据的排序方式,多个键可以按照多个键分别排序;
       使用:job.setSortComparatorClass方法
    6、 Combiner:Reduce中的一个方法,非必须,用于减少传输到Reduce节点的网络消耗,可以把各个分区中的很多键值对的重复部分进行键值对聚集
       使用:job.setCombinerClass方法:设置Combiner执行类
           job.setCombinerKeyGroupingComparatorClass方法:设置Combiner参照的Key比较方式,这个比较中相等的会合并在一起

       默认:将相同key的多条记录合并成一条记录,例如a=1,a=2,a=3这三个key一样的键值对合并成a=[1,2,3]
    7、 压缩:把Map要传输到Reduce的最后的文件进行压缩,之后在Reduce端解压缩即可
       使用:Configuration对象中设置相应的属性
           mapred.compress.map.output:Map任务输出开关
           mapred.map.output.compression.codec:压缩执行的Java类
    到此为止,Map端的处理就完成了,生成了一个最后的文件等待传送到Reduce端进行处理,注意!根据我前面的文章中MapReduce的详细过程,Map溢写出的多个文件的可以根据分区进行合并,合并过程中Merge、Sort、Combiner、压缩是合在一起执行的,这样减少了对磁盘的IO读写次数,提高性能。

Reducer阶段:
    1、 解压缩:如果Map有进行压缩的话在这里解压缩
        使用:压缩的Java类在Map压缩时设置好了。。。
    2、 Merge:由于一个Reduce收到多个Map发送过来的文件,因此要把多个Map的文件合并了再处理;
    3、 SecondSort:由于多个Map中的数据各自是有序的,但是各自之间合并之后的顺序不一定有序(参照归并排序算法),因此还要再排序一次,和Map的排序一样
    4、 GroupSort:类似Map的Combiner,根据Key对相应的比较器比较进行分组,同一个组的一起发送给Reduce,在同一个reduce函数过程中执行
        使用:job.setGroupingComparatorClass方法
    5、 Reduce:根据传进来的Key-ValuesList对,对同一个分组相同Key的键值对们进行相应的处理并输出到结果目录中,比如统计相同Key的这些键值对的最大值、平均值等
        使用:job.setReducerClass方法
    6、 输出到HDFS:重写OutputFormat抽象类(三个方法,分别如下)
         checkOutputSpecs:检查设置的输出目录在HDFS是否已存在,存在则抛异常
         getOutputCommitter:得到输出提交对象,负责中间缓存文件,最后清理等一系列工作
         getRecordWriter:主要!可以定义一个Writer,MapReduce通过这个Writer进行最后的输出,可以定义一个基于本地文件系统的Writer同样可以实现输出到本地文件系统中,通常使用HDFS的JavaAPI输出到HDFS中。
       已有实现:
         SequenceFileOutputFormat:输出成二进制还没序列化的形式
         TextOutputFormat:输出K-V文本,\t隔开
         MultiFileInputFormat:多文件的TextOutputFormat,一个reduce一个文件
         NullOutputFormat:不输出到HDFS,可以在reduce自定义输出目的地,如本地文件系统
       使用:job.setOutputFormatClass方法

    到此为止,MapReduce执行完成,输出到输出目录的结果文件可以作为最后的输出结果进行查看或者作为新的一个MapReduce作业的输入文件。注意!上面的解压缩、Merge、SecondSort、GroupSort这些过程是一起执行的,减少了对磁盘的IO操作从而提升效率。


MapReduce编程优化:
    1、 序列化和反序列化(根据实际业务需求做出相应的优化的话可以极大提升效率)
    2、 网络数据传输数据压缩(一下属性可以通过FileOutputFormat设置实现)
       Mapred.output.compress:是否压缩
       Mapred.output.compression.codec:压缩对应的Java类(最好原生实现)
       Mapred.output.compression.type:压缩类型(NONE,BLOCK,RECORD)
       Mapred.compress.map.output:Map任务输出开关
       Mapred.map.output.compression.codec:Map任务输出对应Java类
    3、 对象比较:
          普通实现
          字节流直接比较实现:相比普通实现,省略掉了将字节流反序列化为对象再进行比较的过程,效率提高。
    4、 Mapper和Reducer 的数量合理:
       Mapper:Mapper对象创建和销毁需要开销,Mapper的执行任务量要够一定的量才让这些开销有价值,通常为让每个Mapper执行能够1分钟左右;
       Reducer:数量多速度快,集群利用率高,但是要预留一定的剩余量以应对推测执行阶段所需要的额外的Reducer节点。
    5、 Combiner减少网络传输
    6、 调整Shuffle
    7、 对象重用:传输实体对象在运行过程中是重用的,因为每个实体提交给相应的Map或Reduce过程之后对象不再使用,这个对象可以填充新的值让下一个任务继续执行使用,减少对象创建的资源开销。


额外补充:
    1. 多键组合的KeyPair:实现WritableComparable接口,重写下列方法,即可完成该组合键的定义:
         write:序列化
         readFields:反序列化
         toString:输出使用
         equals:规范,要和hashCode对应
         hashCode:reduce分区
         compareTo:排序比较
    2. 计数器
    3. 设置调度器:mapred.jobtracker.taskScheduler


本文为博主原创,允许转载,但请声明原文地址:http://www.coselding.cn/article/2016/05/31/MapReduce过程详细解析和使用/

0 0
原创粉丝点击