Hadoop Mapreduce 学习

1 MapReduce2

1.1概述

        Hadoop MapReduce是一个软件框架,可以以一种可靠的、容错的方式在大型集群(数千个节点)上并行处理大量数据(TB字节数据集)的应用程序。MapReduce作业通常将输入数据集分割成独立的区块,由map任务以完全并行的方式处理这些区块。框架对Map的输出会进行排序,然后将其输入到reduce任务中。通常作业的输入和输出都存储在文件系统中。框架负责调度任务、监视它们并重新执行失败的任务。

        通常计算节点和存储节点是相同的,也就是说,MapReduce框架和Hadoop分布式文件系统(参见HDFS架构指南)运行在同一组节点上。这种配置允许框架在数据已经存在的节点上有效地调度任务,从而在整个集群中产生非常高的聚合带宽。MapReduce框架由单个主ResourceManager、worker节点的 NodeManager和每个应用程序的MRAppMaster组成(参见纱线架构指南)。应用程序至少指定输入/输出位置,并通过适当的接口和/或抽象类的实现提供map和reduce函数。这些和其他job参数组成了job配置。

Hadoop 客户端提交Job和配置信息给ResourceManger,它将负责把配置信息分配给从属节点,调度任务并且监控它们,把状态信息和诊断信息传输给客户端。

1.2 MapReduce2 输入和输出

       MapReduce框架只对键、值进行操作。对,也就是说,框架将job的输入视为 <key, value> 键值对。并生成一组 <key, value> 的键值对作为job的输出,可以输出输出键值对可以是不同类型的。

        MapReduce 框架必须将键和值类序列化,因此需要实现Writable接口。此外,key的类必须实现WritableComparable接口,以方便框架排序。

MapReduce作业的输入和输出类型:

(input) <k1, v1> -> map -> <k2, v2> -> combine -> <k2, v2> -> reduce -> <k3, v3> (output)

1.3 Maper及Maper数量

        Mapper 主要功能就是把输入的 key/value对的数据映射成中间的 key/value 对;通过Job的InputFormat 的产生的每个InputSplit都会产生一个 Map;通过Job.setMapperClass(Class) 的方法可以传入自己的Maper实现;对于任务中的每个InputSplit,MapReduce 框架都会调用map(WritableComparable, Writable, Context) 处理InputSplit 的 key/value 数据;同时用户可以覆盖cleanup(Context)方法来执行任何必要的清理;

        应用程序也可以调用Counter 来做一些统计 ;

        MapReduce 框架默认会把Maper的中间结果按照Key分组,分组后的结果会传递至Reduce后输出最终结果;当然用户也可以通过Job.setGroupingComparatorClass(Class).  来自己指定分组的比较器(Comparator );

        Maper 输出的中间结果在Reducer中默认会被排序及分区,分区的总数和reduce的任务数相同;当然用户也可以通过实现Partitioner来控制哪个key进入哪个Reducer中;

        用户也可以通过Job.setCombinerClass(Class)来指定combiner来实现对本地中间结果做一些聚合操作,以此减少从Maper进入Reducer的数据量;
       Maper输出的中间结果往往以简单的方式来排序,比如key-len, key, value-len, value。同时用户也可以通过Configuration的 CompressionCodec来控制中间输出结果是否启动压缩;

Map的个数通常由输入文件的总大小决定,即由输入文件块的总个数决定;Map在每个节点上并行度最好在10到100之间,不过对于cpu级别非常低的映射任务,它已经被设置为300。任务设置需要一段时间,所以最好执行映射至少需要一分钟。因此,如果有10Tb的数据,且块的大小为128M,那将会产生8200个Map,除非通过Configuration.set(MRJobConfig.NUM_MAPS, int)(参数只供系统参考)来设置更高的Map个数;

Map 主要参数:

     从Map映射出的记录会被存储在序列化缓冲去中,元数据会存储到 accounting 缓冲区中。当序列化缓冲区或元数据超过阈值时,缓冲区的内容将被排序并在后台写入磁盘,同时Map任务将继续输出记录。如果任意一个缓冲区在溢出过程中被完全填满,map线程将阻塞。当映射完成时,所有剩余的记录都写到磁盘上,磁盘上的所有段合并到一个文件中。最小化磁盘溢出数量可以减少映射时间,但是更大的缓冲区也会减少Mapper可用的内存。

NameTypeDescription
mapreduce.task.io.sort.mbintThe cumulative size of the serialization and accounting buffers storing records emitted from the map, in megabytes.
mapreduce.map.sort.spill.percentfloatThe soft limit in the serialization buffer. Once reached, a thread will begin to spill the contents to disk in the background.

1):如果在spill 过程中超过了阈值,则收集将继续,直到溢出完成。例如,如果mapreduce.map.sort.spill。如果百分比设置为0.33,并且缓冲区的其余部分在溢出运行时被填满,下一次溢出将包括所有收集的记录,或达到缓冲区的容量的66%,则不会产生额外的溢出。换句话说,阈值是定义触发器,而不是阻塞触发器。

2):如果记录数据大于序列化缓冲区,则会触发溢出操作,将内容溢出到不同的文件中;

1.4 Reducer及Reducer数量

        Reducer 将Maper产生的中间值聚合为更小的基于Key的集合,通过Job.setNumReduceTasks(int).可以设置reduce的个数;通过 Job.setReducerClass(Class)方法可以设置Reducer的实现方法及控制它的的初始化;之后MapReduce框架会调用 reduce(WritableComparable, Iterable<Writable>, Context)  方法处理每个<key, (list of values)>数据对,用户也可以覆盖cleanup(Context)方法来执行任何必要的清理;

Reducer 有三个主要的阶段:1、shuffle;2、 sort ;3、reduce。

Shuffle:输入到Reducer的Maper数据会被排序,且在这一阶段 MapReduce 框架会通过Http获取所有Maper相关的所有分区输出数据;

Sort :这一阶段,Reducer 会按照输入数据的Key进行分组(不同Maper可能产生相同的key)。shuffle和sort基本同时进行,即当Map的输出结果在获取时就被合并了;

Secondary Sort:如果在reduce之前需要中间key的比较规则与Reduce需要的分组的比较规则不同,则可以通过Job.setSortComparatorClass(Class) 指定一个比较器。由于Job.setGroupingComparatorClass(类)可用于控制如何对中间键进行分组,因此可以结合使用它们来模拟对值的二次排序。(Secondary Sort意味着首先按照第一字段排序,然后再对第一字段相同的行按照第二字段排序,注意不能破坏第一次排序 的结果 );

Reduce: 这个阶段对于每个分组后的 <key, (list of values)> 键值对,reduce(WritableComparable, Iterable<Writable>, Context)方法会被调用,输出结果通过Context.write(WritableComparable, Writable)方法写入文件系统;

同时可以采用counter统计应用程序的数据;

Reducer 的结果不会再排序;

Reduce数目:Reduce比较恰当的数目在 0.95 或者 1.75 *(节点个数*每个节点最大container的个数)。选择0.95参数则reduce会在Map任务执行完后立马执行;选择1.75则任务执行较快的节点在执行完第一次reduce操作后,会启动第二轮的reduce操作,负载均衡较好;虽然增加reduce个数将增加系统的资源消耗,但是 可以提高负载均衡能力和减小任务失败的几率;上面的比例因子略小于整数,以便在框架中为推测任务和失败任务保留一些reduce位置。

如果没有Reudce操作,则Reducer的任务个数也可以设置成0;如果是这种情况,则Map的输出结果将会直接写入到文件系统中,框架不会排序map结果,直到写入文件系统前才会排序;

Partitioner控制Map输出的中间结果key的分区;key或者子key通常按照Hash函数来获取分区;往往分区的个数和Reduce个数是相同的;也就意味着Partitioner 控制着到底哪些key会被送到对应的Reducer中;HashPartitioner 是系统默认的 Partitioner.

Counter是用来追踪MapReduce框架的任务的。Mapper 和 Reducer 实现可以通过Counter来追踪任务的进度; Hadoop MapReduce 库集成了 mappers, reducers, 和partitioners等很多有用的库;


Shuffle/Reduce 参数:

每个reduce通过HTTP将Partitioner对应的输出提取到内存中,并定期将这些输出合并到磁盘上。如果打开了映射输出的中间数据压缩功能,则每个输出结果在进入Reduce时都会被解压到内存中。以下选项会影响在减少之前将这些合并到磁盘的频率,以及在减少期间分配给映射输出的内存。

NameTypeDescription
mapreduce.task.io.soft.factorintSpecifies the number of segments on disk to be merged at the same time. It limits the number of open files and compression codecs during merge. If the number of files exceeds this limit, the merge will proceed in several passes. Though this limit also applies to the map, most jobs should be configured so that hitting this limit is unlikely there.
mapreduce.reduce.merge.inmem.thresholdsintThe number of sorted map outputs fetched into memory before being merged to disk. Like the spill thresholds in the preceding note, this is not defining a unit of partition, but a trigger. In practice, this is usually set very high (1000) or disabled (0), since merging in-memory segments is often less expensive than merging from disk (see notes following this table). This threshold influences only the frequency of in-memory merges during the shuffle.
mapreduce.reduce.shuffle.merge.percentfloatThe memory threshold for fetched map outputs before an in-memory merge is started, expressed as a percentage of memory allocated to storing map outputs in memory. Since map outputs that can’t fit in memory can be stalled, setting this high may decrease parallelism between the fetch and merge. Conversely, values as high as 1.0 have been effective for reduces whose input can fit entirely in memory. This parameter influences only the frequency of in-memory merges during the shuffle.
mapreduce.reduce.shuffle.input.buffer.percentfloatThe percentage of memory- relative to the maximum heapsize as typically specified in mapreduce.reduce.java.opts- that can be allocated to storing map outputs during the shuffle. Though some memory should be set aside for the framework, in general it is advantageous to set this high enough to store large and numerous map outputs.
mapreduce.reduce.input.buffer.percentfloat

The percentage of memory relative to the maximum heapsize in which map outputs may be retained during the reduce. When the reduce begins, map outputs will be merged to disk until those that remain are under the resource limit this defines. By default, all map outputs are merged to disk before the reduce begins to maximize the memory available to the reduce. For less memory-intensive reduces, this should be increased to avoid trips to disk.

 

1)如果一个Map的输出大于分配给复制Map 25%的内存,那么Map结果将跳过内存这一层,直接写入磁盘;

2)当启动combiner 运行时,高合并阈值和大缓冲区不一定有效。对于在获取所有映射输出之前就开始合并的情况,combiner 会在溢出到磁盘时运行。在某些情况下,可以通过使用combiner来合并Map输出(使磁盘溢出更小,且能够并行溢出)来减少reduce时间,而不是只增加缓冲区大小。

3)当将内存映射输出合并到磁盘然后reduce时,因为有segments要溢出,所以中间合并很有必要。

3 MapReduce2 架构

3.1 MapReduce 任务提交

  • 提交MapReduce 任务到客户端Client;
  • YARN 资源管理器 Resource Manager 负责协调计算机上的资源的分配;
  • YARN 节点管理器 Node Manager 负责启动和监视集群上计算容器 container;
  • MapReduce 应用程序MRAppMater 负责协调和运行MapReduce的job,MRAppMater和MapReduce任务在container中进行,且这些container由资源管理器Resource Manager  分配并由节点管理器Node Manager 管理;
  • Hdfs 用来共享和其他实体之间得作业和文件;

整体提交任务过程具体如下如所示:

在这里插入图片描述

3.2 Map 数据处理

1.每个输入分片会让一个map任务来处理,默认情况下,以HDFS的一个块的大小(默认为128M)为一个分片,当然我们也可以设置块的大小。map输出的结果会暂且放在一个环形内存缓冲区中(该缓冲区的大小默认为100M,由mapreduce.task.io.sort.mb属性控制),当该缓冲区快要溢出时(默认为缓冲区大小的80%,由mapreduce.map.sort.spill.percent属性控制),会在本地文件系统中创建一个溢出文件,将该缓冲区中的数据写入这个文件。

2.在写入磁盘之前,线程首先根据reduce任务的数目将数据划分为相同数目的分区,也就是一个reduce任务对应一个分区的数据。这样做是为了避免有些reduce任务分配到大量数据,而有些reduce任务却分到很少数据,甚至没有分到数据。其实分区就是对数据进行hash的过程。然后对每个分区中的数据进行排序,如果此时设置了Combiner,将排序后的结果进行合并操作,以便尽可能少的数据写入到磁盘及减少输入Reduce的数据量。

3.当map任务输出最后一个记录时,可能会有很多的溢出文件,这时需要将这些文件合并。合并的过程中会不断地进行排序和合并操作,目的有两个:1.尽量减少每次写入磁盘的数据量;2.尽量减少下一复制阶段网络传输的数据量。最后合并成了一个已分区且已排序的文件。为了减少网络传输的数据量,可以选择将数据压缩,只要将mapreduce.map.output.compress 设置为true就可以了。

4.将分区中的数据拷贝给相对应的reduce任务。map任务一直和其父TaskTracker保持联系,而TaskTracker又一直和JobTracker保持心跳。所以JobTracker中保存了整个集群中的运行信息。reduce任务可以向JobTracker获取对应的map输出位置。

3.3 Reduce 数据处理

1.Reduce会接收到不同map任务传来的数据,并且每个map传来的数据都是有序的。如果reduce端接受的数据量相当小,则直接存储在内存中(缓冲区大小由mapred.job.shuffle.input.buffer.percent属性控制,表示用作此用途的堆空间的百分比),如果数据量超过了该缓冲区大小的一定比例(由mapred.job.shuffle.merge.percent决定),则对数据合并后溢写到磁盘中。

2.随着溢写文件的增多,后台线程会将它们合并成一个更大的有序的文件,这样做是为了给后面的合并节省时间。其实不管在map端还是reduce端,MapReduce都是反复地执行排序,合并操作。

3.合并的过程中会产生许多的中间文件(写入磁盘了),但MapReduce会让写入磁盘的数据尽可能地少,并且最后一次合并的结果并没有写入磁盘,而是直接输入到reduce函数。

加Combiner:

 

4 Shuffle 

4.1 Shuffle 设计理念

在Hadoop这样的集群环境中,大部分map task与reduce task的执行是在不同的节点上。当然很多情况下Reduce执行时需要跨节点去拉取其它节点上的map task结果。如果集群正在运行的job有很多,那么task的正常执行对集群内部的网络资源消耗会很严重。这种网络消耗是正常的,我们不能限制,能做的就是最大化地减少不必要的消耗。还有在节点内,相比于内存,磁盘IO对job完成时间的影响也是可观的。从最基本的要求来说,我们对Shuffle过程的期望可以有: 

  • 完整地从map task端拉取数据到reduce 端。
  • 在跨节点拉取数据时,尽可能地减少对带宽的不必要消耗。
  • 减少磁盘IO对task执行的影响。

4.2 Shuffle 机制

Shuffle过程是MapReduce的核心,也被称为奇迹发生的地方。要想理解MapReduce, Shuffle是必须要了解的。Shuffle的正常意思是洗牌或弄乱,可能大家更熟悉的是Java API里的Collections.shuffle(List)方法,它会随机地打乱参数list里的元素顺序。如果你不知道MapReduce里Shuffle是什么,那么请看这张图: 


      MapReduce提供Partitioner接口,它的作用就是根据key或value及reduce的数量来决定当前的这对输出数据最终应该交由哪个reduce task处理。默认对key hash后再以reduce task数量取模。默认的取模方式只是为了平均reduce的处理能力,如果用户自己对Partitioner有需求,可以订制并设置到job上

shuffle阶段又可以分为Map端的shuffle和Reduce端的shuffle。

  一、Map端的shuffle

①分区partition

②写入环形内存缓冲区(accounting buffer)

③执行溢出写

        排序sort--->合并combiner--->生成溢出写文件

④归并merge

Map端会处理输入数据并产生中间结果,这个中间结果会写到本地磁盘,而不是HDFS。每个Map的输出会先写到内存缓冲区中,当写入的数据达到设定的阈值时,系统将会启动一个线程将缓冲区的数据写到磁盘,这个过程叫做spill。

  在spill写入之前,会先进行二次排序,首先根据数据所属的partition进行排序,然后每个partition中的数据再按key来排序。partition的目是将记录划分到不同的Reducer上去,以期望能够达到负载均衡,以后的Reducer就会根据partition来读取自己对应的数据。接着运行combiner(如果设置了的话),combiner的本质也是一个Reducer,其目的是对将要写入到磁盘上的文件先进行一次处理,这样,写入到磁盘的数据量就会减少。最后将数据写到本地磁盘产生spill文件(spill文件保存在{mapred.local.dir}指定的目录中,Map任务结束后就会被删除)。

  最后,每个Map任务可能产生多个spill文件,在每个Map任务完成前,会通过多路归并算法将这些spill文件归并成一个文件。至此,Map的shuffle过程就结束了。
    简单地说,reduce task在执行之前的工作就是不断地拉取当前job里每个map task的最终结果,然后对从不同地方拉取过来的数据不断地做merge,也最终形成一个文件作为reduce task的输入文件。见下图: 

Reduce端的shuffle主要包括三个阶段,copy、sort(merge)和reduce。
    下面我也分段地描述reduce 端的Shuffle细节: 
   (1)copy阶段: 首先要将Map端产生的输出文件拷贝到Reduce端,但每个Reducer如何知道自己应该处理哪些数据呢?因为Map端进行partition的时候,实际上就相当于指定了每个Reducer要处理的数据(partition就对应了Reducer),所以Reducer在拷贝数据的时候只需拷贝与自己对应的partition中的数据即可。ReduceTask 从各个 MapTask 上远程拷贝一片数据,并针对某一片数
据,如果其大小超过一定阈值,则写到磁盘上,否则直接放到内存中。
  (2)Merge 阶段:在远程拷贝数据的同时,ReduceTask 启动了两个后台线程对内存和磁盘上的文件进行合并,以防止内存使用过多或磁盘上文件过多。
  (3)Sort 阶段:按照 MapReduce 语义,用户编写 reduce()函数输入数据是按 key 进行聚集的一组数据。为了将 key 相同的数据聚在一起,Hadoop 采用了基于排序的策略。由于各个 MapTask 已经实现对自己的处理结果进行了局部排序,因此,ReduceTask 只需对所有数据进行一次归并排序即可。
  (4)Reduce 阶段:reduce()函数将计算结果写到 HDFS 上。

 

参考文章:

https://blog.csdn.net/fanxin_i/article/details/80388221

https://hadoop.apache.org/docs/stable/hadoop-mapreduce-client/hadoop-mapreduce-client-core/MapReduceTutorial.html

https://blog.csdn.net/ASN_forever/article/details/81233547?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值