TextInputFormat切片机制
TextInputFormat是FileInputFormat的若干个实现类之一。
首先,要明确的是,TextInputFormat在切片时不考虑数据集整体,也就是不考虑输入路径下其他文件,而是逐个对每一个文件单独切片。
它在读取单个文件数据时是按行读取的,其用来存储读取的数据的数据结构固然是键值对,键存储的是该行在整个文件中的起始字节的偏移量,键的数据类型是LongWritable。而值是这行的内容,不包括任何的行终止符,即换行符和回车符,值的数据类型是Text。下面给一个例子
#文本内容
abc def ghi
hello world
Sadge Sadge Sadge
#键值对内容
< 0, abc def ghi >
< 13, hello world >
< 26, Sadge Sadge Sadge>
以上便是很常见的TextInputFormat,同时也是框架默认选择切片策略,接下来会说明一下CombineTextInputFormat。
CombineTextInputFormat切片机制
如果对每个文件都单独做一个切片然后再开启MapTask,那么如果面临过多的小文件时,比如1万个1kb的小文件,那么显然再用这种一个文件一个片的策略有些过于愚蠢了,要为每一个小文件都花上大量的资源去开启一个Task。
这个时候就需要在逻辑上对小文件进行合并,然后再做切片处理,CombineTextInputFormat的切片策略就是这样。CombineTextInputFormat可以将许多小文件在逻辑上规划到一个切片中,然后这样一个MapTask就可以一起处理多个小文件了。
接下来说明一下CombineTextInputFormat的切片流程。
它的切片流程略微复杂一些,其可以划分为两部分,虚拟存储过程和切片合并过程。
在切片前,开发者会设定好一个虚拟存储切片最大值,具体代码会在后面一起说明,而这个值设置多少最好根据文件实际情况来判断设置多少。
对一列文件,先拆分文件,目的是将拆成小文件,当然这个过程是虚拟的,是通过操作索引来完成的。
拆分策略是这样的,如果这个文件大于两倍的最大值(虚拟存储切片最大值),那么就先切出来一份最大值大小的片。如果这个文件大于一倍的最大值,但小于两倍,那么就均分这个文件(从中间劈开),目的是防止出现零头的情况。小于一倍的不切。那么这就是拆分过程。
这个时候问题就来了,如果全称这么做,文件会不会太碎了。然后这个时候第二个过程就起作用了。即将虚拟文件大小按顺序前后相加。看是否大于最大值,一旦大于,那么将这几个虚拟文件作为一个切片,如果不大于,那么则继续加上下一个虚拟存储文件,并形成一个切片。
来一个例子
最大值为4M,比如说有文件大小
1.5M 6.2M 7.8M 4.2M 1M。
那么第一个过程之后
1.5M 3.1M 3.1M 3.9M 3.9M 2.1M 2.1M 1M
第二个过程则是
(1.5M 3.1M)(3.1M 3.9M)(3.9M 2.1M)(2.1M 1M)
划分完毕
CombineTextInputFormat实现
在之前编写的Driver的main中,在提交job前的地方添加如下代码
// 默认用的是TextInputFormat.class
job.setInputFormatClass(CombineTextInputFormat.class);
// 根据需要改成需要的值,这里虚拟存储切片最大值设置的是4M
CombineTextInputFormat.setMaxInputSplitSize(job, 4194304);