Hadoop InputFormat记录

InputFormat:主要用于描述输入数据的格式,它提供了三个功能:

(1)Validate the input-specification of the job.

(2)Split-up the input file(s) into logical {@link InputSplit}s, each of which is then assigned to an individual {@link Mapper}.

(3)Provide the {@link RecordReader} implementation to be used to glean input records from the logical <code>InputSplit</code> for processing by the {@link Mapper}.

(1)为Job验证输入。

(2)数据切分:按照某个策略将输入数据切分成若干个split,以便确定Map Task个数以及对应的split。

(3)为Mapper提供输入数据:给定某个split将其解析成一个个key/value对。

<span style="font-size:18px;">public interface InputFormat<K, V> {
  InputSplit[] getSplits(JobConf job, int numSplits) throws IOException;
  RecordReader<K, V> getRecordReader(InputSplit split,
                                     JobConf job, 
                                     Reporter reporter) throws IOException;
}</span>

getSplits方法主要完成数据切分的功能,将输入数据切分成numSplits个InputSplit。

getRecordReader方法返回一个RecordReader对象,该对象可将输入的InputSplit解析成若干个key/value对。


所有基于文件的InputFormat实现的基类是FileInputFormat。整个基于文件的InoutFormat体系的设计思路是,由公共基类FileInputFormat采用统一的方法(文件切分算法)对各种输入文件进行切分,比如按照某个固定大小等分,而由各个派生的InputFormat自己提供机制将进一步解析InputSplit。比如:FileInputFormat继承自InputFormat,但是只是实现了getSplits方法,另一个获取读取器的方法没有实现,这样做是有道理的,因为很多不同格式的文件需要使用不同的读取器来提取数据,比如lzo压缩后的文件的读取器,要先解压后才能读取。

对应到具体的实现是,

(1)基类FileInputFormat提供getSplits实现(主要算法:文件切分算法、hosts选择算法)

(2)而派生类提供getRecordReader实现。

详细讲解下这两步:

(1)关于getSplits方法:

ps:file(文件大小)、block(块大小)、split(分片大小)的关系:

比如设定block=64MB,如果file1=20MB,file2=100MB,则file1有1个块,block1=20MB,file2有2个块,block2=64MB,block3=36MB,分片大小的确定由文件切分算法实现,即分片的大小可能比块的大小大或小。

待InputSplit切分方案确定后,接下来要确定每个InputSplit的元数据信息:InputSplit <file,start,length,hosts>分别表示该InputSplit所在的文件、起始位置、长度、所在的hosts(节点)列表。

确定每个InputSplit的所在hosts列表由hosts选择算法实现。总而言之:当使用FileInputFormat实现InputFormat时,为了提高Map Task的数据本地性,应尽量使InputSplit大小与block大小相同。

源代码中形成splits列表的逻辑大概是这样的:
首先会从 job 对象中所有的输入文件的列表提取出来,List<FileStatus> files = listStatus(job);

然后就要对每个文件进行逻辑分片了,分片的逻辑大概是这样的:首先,计算这个文件的长度(按照字节),然后将这个文件的块信息拿出来。如果这个文件可以被分片并且长度不是0,那么就开始进行逻辑分片。每个分片的大小通过函数computeSplitSize来计算。

然后如果文件的剩余长度是分块的1.1倍以上的话,就创建一个新分片:
splits.add(new FileSplit(path, length-bytesRemaining, splitSize, blkLocations[blkIndex].getHosts()));
进而,将剩余长度减去已经被分配掉的splitSize,这样循环直到不满足条件。

等循环完成之后,如果还有剩余的部分,那么剩下就可以再做一个分片,加入到列表中。

但是,如果我们一开始输入的文件的大小是不可分割的话,那么我们就把整个文件作为一个分片,形成一个实例:
splits.add(new FileSplit(path, 0, length, blkLocations[0].getHosts()));
如果这个文件是可分割的,但是长度是0,也做一个默认的分片:
splits.add(new FileSplit(path, 0, length, new String[0]));

文件的分片列表就产生了,然后读取器就可以从这些分片中按照相应的读取逻辑来读取数据,并交给mapper进行处理了。

(2)关于getRecordReader方法:

由FileInputFormat的派生类实现getRecordReader函数,该函数返回一个RecordReader对象,它实现了类似于迭代器的功能,将某个InputSplit解析成一个个kv对,具体实现时应考虑以下两点:

a. 定位边界记录:比如对于TextInpuTFormat,每两条记录之间存在换行符。注意的是,由于FileInputFormat仅仅按照数据量对文件进行切分,因而InputSplit的第一条记录和最后一条记录可能会被中间切开,为了解决这种记录跨越InputSplit读取问题,RecordReader规定每个InputSplit的第一条不完整记录给前一个InputSplit处理。

b. 用什么规则解析成kv对:比如对于TextInputFormat,每一行的内容即为value,而该行在整个文件中的偏移量为key。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值