mapreduce 过程中关于 0-length 数据进入reducer 时的处理

来源:互联网 发布:ubuntu安装office2013 编辑:程序博客网 时间:2024/06/10 02:21

当MapOutputBuffer对象中的kvbuffer缓冲区即将到达spill的标准时(有两种标准:情况一.key/value 占用空间 >= io.sort.mb * io.sort.spill.percent * ( 1 - io.sort.record.percent )   或者 情况二. key/value 的个数 *16 byte  >=  io.sort.mb * io.sort.spill.percent * io.sort.record.percent ),进行spill时,会调用sortAndSpill()函数。sortAndSpill()函数中,使用kvoffset,kvindices,kvbuffer三个环形缓冲区对kvbuffer中的所有 key/value 数据进行排序。


这是一个二级排序,排序的规则是优先对partitionID排序,具有相同的partitionID时,按照key排序。 排序之后,讲kvbuffer中的数据写入文件,这时写入的数据是有序的。每当一个partition相同的所有数据写完之后,IFile.Writer会关闭,写下两个特殊EOF字节(-1,-1),标志着这个partition的数据在此结束,之后的数据属于下一个partition,并且IndexRecord对象用来索引,记录下该partition在该文件中的[起始偏移startOffset, 原始数据长度rawLength,压缩后数据长度partLength]。这样,在之后TaskTracker来取指定partition数据的时候,可以直接通过索引文件找到该数据文件中属于指定partition的数据块。这是一个在面对大数据量时相当成熟的设计。


这是一次spill过程,当出现多次spill时,需要使用Merger.merge()函数对所有的spill文件进行合并。Merger.merge()的设计也非常优秀。仔细阅读其中的代码后,感觉收获了很多。


在spill过程中会出现一种特殊情况,若这个mapper所处理的所有数据都没有一个落入一个指定的reducer (该过程由partitioner计算),那么在merge了所有的spill文件之后所产生的最终的数据文件file.out中,就会连续出现4个结束符,因为前后各两个结束符之间没有 key/value 数据。但是这种情况下,因为有2个结束符占用2个byte的关系,索引文件依然会为这一段数据做索引,会认为这一个partition的数据有2个byte。


那么因为索引文件为该partition数据做了索引(2个EOF,2个byte数据),在shuffle发生时,TaskTracker依然会将该段数据(2个EOF字节)当做mapOutput发送给reducer,事实上这只是一段没有key/value的垃圾数据(会在后面解释为什么)。


reducer通过TaskTracker获得了所有mapper的输出片段之后,会将他们组成 segmentList,并且传给Merger.merge()函数,该函数使用堆排序进行归并排序。


那么那个没有实际数据只有2个EOF的mapOutput会被怎么处理呢?

在Merger.merge()函数中对他们做了很好的处理。在merger发生之前,每个segment会使用IFile.Writer读取自己数据的第一个 key/value 对,这时,IFile.Writer读取第一个 key/value 时发现只有两个 EOF, 那么Merger.merge()函数就知道这是一段空数据,就会自动抛弃这个segment。


shuffle过程确实传送了垃圾数据给reducer, 但由于merge()中对于空数据的处理,使得整个shuffle过程一致且同一,不需要对空数据段做特殊处理。



0 0