我们在本节的目标是关注数据的归并过程,包括Partitioner、Combiner、Shuffle&Sort以及OutputFormat等组件合过程。
对于Partioner,我们将讨论什么是Hadoop Partitioner。MapReduce中的Partitioner操作mapper输出的中间数据,对它们的key进行分区。通过哈希函数,使用key或者key的子集得到分区。总分区数取决于reduce task(任务)的数量。我们还将学习为什么需要Hadoop partitioner,默认Hadoop partitioner是什么,在Hadoop中,什么是分区不均,如何克服。
Hadoop Combiner也称为“Mini-Reducer”,它在将数据传递给Reducer之前,对Mapper输出记录中同一个key的数据进行合并处理。我们将介绍什么是Hadoop combiner,带有以及不带有combiner的MapReduce程序的区别,Hadoop combiner的优缺点。
在Hadoop中,将mapper输出的中间数据传输到reducer的过程叫做shuffle。Reducer的实质就是将一个或者多个key和value相关联。mapper生成的中间键值对自动按key排序。我们在本节将讨论Hadoop MapReduce有关shuffle和Sort(排序)的细节。
在这里,我们也将会讨论什么是Sort(排序),什么是shuffle,在MapReduce中Shuffle和sort阶段的目的,Shuffle是如何工作的,sort是如何工作的,我们还会了解MapReduce中的次排序。
Hadoop输出格式会检查作业的输出规范。它决定如何实现RecordWriter,向输出文件写入输出内容。在本节,我们将了解Hadoop输出格式,了解什么是RecordWriter,如何使用RecordWriter。我们也将讨论Hadoop的各种输出格式类型,比如:textOutputFormat、sequenceFileOutputFormat、mapFileOutputFormat、sequenceFileAsBinaryOutputFormat、DBOutputFormat、LazyOutputFormat以及 MultipleOutputs等。
在我们开始介绍MapReduce partitioner之前,我们先理解什么是Hadoop mapper,Hadoop Reducer以及Hadoop combiner。
map输出的中间数据其key的分区是由Partitioner控制的。通过哈希函数,使用key或者key的子集得到分区。每个mapper的输出,将根据键值对进行分区,key相同的记录会分到同一个分区,然后每个分区发送到一个reducer。分区类负责决定一个给定的键值对去哪个分区。Partition(分区)阶段发生在map阶段之后,reduce阶段之前。接下来,我们来介绍为什么需要MapReduce分区。
MapReduce job接收输入数据集,然后数据被分片,作为map的输入,在map阶段产生键值对列表,每个task处理一个分片,而每个map会输出一个键值对列表,map阶段的输出会发送给Reduce task,reduce task对map输出执行用户定义的reduce函数。但是,在reduce阶段之前,将会基于key对map输出进行分区和排序。
分区指的是对每个key的所有值都分到一组,确保某个key的所有值都进入同一个reducer进行处理,这样,便于map输出在reducer上进行分布。
Hadoop MapReduce Partitioner将mapper输出重新分配到reducer,以便使某个reducer能够处理某个特定的key。
Hadoop MapReduce中,默认的Hadoop Partitioner是Hash Partitioner,它计算key的哈希值,然后基于计算结果指派分区。
在Hadoop中运行的Partitioner数量等于reducer数,也就是说,Partitioner根据reducer数(通过JobConf.setNumReduceTasks()方法进行设置)来创建分区。因此,一个reducer处理来自一个Partitioner的数据。同时,当有多个reducer时,才会创建Partitioner。
如果在输入数据中,某个key的数据多于其他任何一个key,在这种情况下,我们应用两种机制来将数据发送到分区:
但是,如果hashCode()方法不能在分区范围内均匀分配其他key的数据,那么发送到reducer的数据就会不平衡。数据分区不平衡意味着一些reducer的输入数据比其他reducer多,也就是说,这些reducer要做比其他reducer更多的工作。因此,整个job要等待一个reducer完成它的超出负载的工作。
如何克服分区不均?
要克服分区不均,我们需要建立定制的partitioner,通过它在不同的reducer上平均分布负载。
当在大型数据集上运行MapReduce作业时,Mapper会生成大量的中间数据,这些中间数据会传递给reducer做进一步处理,这样会导致严重的网络堵塞。MapReduce框架提供了叫做Hadoop Combiner的功能,它在降低网络阻塞方面扮演着关键角色。
我们在前面已经知道了什么是mapper以及什么是reducer。现在我们来了解什么是Hadoop MapReduce Combiner。
MapReduce中的combiner也称作“Mini-reducer”。Combiner的基本任务是在将数据传递给Reducer之前,对Mapper的输出数据进行处理。它在mapper之后,reducer之前运行,而且它是可选的。
现在,我们来了解Hadoop combiner是如何工作的,以及在MapReduce中使用combiner和不使用combiner有何区别。
当没有使用combiner时,假设输入分为两个mapper,这两个mapper生成9个key。现在我们拥有9个键值对的中间数据,接下来,mapper会将这些数据直接发送给reducer,在向reducer发送数据的过程中,会消耗一些带宽。如果数据量很大,在向reducer传输数据时会消耗更多的时间。
现在,如果我们在mapper和reducer中间使用combiner,那么combiner在将数据发送到reducer之前,会对中间数据(9个键值对)进行shuffle处理,比如最终输出4个键值对。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LSL0MFkN-1575335789753)(images/mapreduce-program-with-combiner.jpg)]
在带有combiner的MapReduce程序中,reducer现在只需要处理来自2个combiner的4个键值对。这样reducer只需要执行4次就可以产生最终的输出,这样提高了整体性能。
我们已经详细介绍了Hadoop MapReduce Combiner,接下来,让我们了解一下MapReduce combiner的一些优点。
当然,Hadoop combiner也存在一些缺点,包括:
在我们开始介绍Shuffle和Sort之前,我们已经了解了MapReduce的其他阶段,比如,Mapper,Reducer,Combiner,partitioner以及inputFormat。
在Hadoop的Shuffle阶段,会将map的输出传输给Reducer。Sort阶段涉及map输出的合并和排序。来自mapper的数据是按key进行分组的,在reducer间分割,并且按key排序。每个reducer会获取同一个key的所有值。Shuffle和sort阶段在MapReduce框架中是同时进行的。
将来自mapper的数据传输到reducer的过程,就是所谓的shuffle。这个过程,系统对map的输出执行排序并传输到reducer作为输入。因此,MapReduce的shuffle阶段对于reducer来说是必须的,否则它们将没有任何输入。shuffle过程甚至会在map阶段完成之前就已经开始了,这样可以节约一些时间,从而节约任务的完成时间。
mapper生成的key会被MapReduce框架自动排序,也就是说,在开始reducer之前,mapper生成的所有中间键值对都会按key,而不是按值进行排序。传递给每个reducer的值不会被排序。
排序有助于reducer轻松决定一个新reduce 任务该何时开始。这样可以为reducer节约时间。如果输入数据已经按key排序,当接下来的key和前一个不同时,Reducer就会知道它该启动一个新的reduce任务。每个reduce任务会接受键值对作为输入,生成输出的键值对。
注意,如果你指定reducer为0(通过setNumReduceTasks(0)方法),则shuffle和sort就根本不会执行。那么,MapReduce作业会在map阶段停止,而且map阶段不会包含任何的排序。
如果我们想对reducer的值进行排序,那我们需要使用secondary sorting(次排序)技术,它允许我们在将数据传输到reducer时对值进行排序(可以是升序或者降序)。
在我们开始介绍Hadoop输出格式之前,我们先来了解什么是RecordWriter,以及它在MapReduce中扮演的角色。
正如我们所知道的,Reducer接受又Mapper产生的一系列中间键值对作为输入,在这些数据上运行reducer函数生成输出,再次产生0或更多的键值对。
RecordWriter将Reducer阶段输出的键值对写入到输出文件。
正如上面我们所提及的,Hadoop RecordWriter从Reducer接受输出数据,然后将这些数据写入到输出文件。RecordWriter将这些输出键值对写入到输出文件的方式是由输出格式决定的。OutFormat和InputFormat在功能上类似。Hadoop提供了OutputFormat实例向HDFS或者本地磁盘写入文件。OutputFormat为Map-Reduce作业描述了输出规范。
**FileOutputFormat.setOutputPath()**方法用于设置输出目录。每个Reducer会在输出目录中写一个单独的文件。
在Hadoop中,有如下各种OutputFormat类型:
TextOutputFormat是MapReduce默认的Hadoop reducer输出类型,它将键值对写入到文本文件中,每一行一个键值对。它的键和值可以是任何类型,TextOutputFormat会调用它们的toString()方法将它们转换成字符串类型。每个键值对都以tab字符分隔,你也可以通过***MapReduce.output.textoutputformat.separator***属性来改变这个分隔符设置。KeyValueTextOutputFormat它会基于配置的分隔符处理键值对。
这个输出格式会将输出写入到sequence文件,它也是MapReduce作业之间使用的中间格式,它可以向文件快速序列化任意数据类型。随着上一个reducer输出这种格式的数据,对于下一个mapper来说,可以使用与其对应的SequenceFileInputFormatkey将文件反系列化成相同的类型。这种输出类型又是易于压缩的,SequenceFileOutputFormat提供的静态方法可以控制其压缩。
它也是SequenceFileInputFormat的一种,它以二进制格式将键和值写入sequence文件。
MapFileOutputFormat在Hadoop输出格式中,属于FileOutputFormat类型,它将输出写入到map文件。在MapFile中,key必须按顺序添加,因此,我们需要确保reducer输出的key是已经排序的。
它将数据写入文件,而文件名取决于输出的键和值或者是任意字符串
有时候,FileOutputFormat会在即使文件为空的情况下也建立输出文件。LazyOutputFormat是OutputFormat的包装,它确保在给定分区有输出记录时才建立输出文件。
DBOutputFormat这种输出格式是将输出写入到关系型数据库和HBase中。它将reduce输出写入到SQL表。它接受键值对,key的类型继承自DBwritable。返回的RecordWriter会将key和值使用批SQL语句写入到数据库。
Hadoop Partitioner允许我们在reducer上面平均分配map输出。Partitioner负责对map输出基于key进行分区和排序。
在降低网络阻塞方面,MapReduce Combiner扮演着关键的角色。MapReduce Combiner对Mapper的输出做归并处理从而整体提高reducer的性能。
shuffle-sort是和归并mapper中间输出结果同时进行的。如果指定reducer为0(可以使用setNumReduceTasks(0)方法),那么shuffle和sort就不会执行。
我们本节还介绍了Hadoop Reducer输出格式,这些输出格式决定了如何将MapReduce作业的结果写入到文件系统。