之前大家对于hadoop中map输入的分片不是很了解,直接认为map输入的分片数是由文件的个数和是否大于block_size来决定map的个数,这样是不准确的,的确在默认的情况下是这样的,但是实际优化过程中,并不是文件越多就起的map就越多,因为小文件太多的话,这样处理会影响系统效率,当然大文件的切分也不一定是按照block_size来切分,今天我们主要看看小文件合并的类CombineFileInputFormat中和切分大文件的FileInputFormat类
1.先看FileInputFormat类
其切分的方法如下:
public List<InputSplit> getSplits(JobContext job) throws IOException {
StopWatch sw = (new StopWatch()).start();//这是监控任务用的
/*这两个值可以通过mapred.min.split和mapred.max.split.size来设置
其中minSize是从1和你设置的mapred.min.split中最大值*/
long minSize = Math.max(this.getFormatMinSplitSize(), getMinSplitSize(job));
long maxSize = getMaxSplitSize(job);
List<InputSplit> splits = new ArrayList();//存储得到的分片结果
List<FileStatus> files = this.listStatus(job);//存储着输入文件列表信息
Iterator i$ = files.iterator(); //创建一个用来遍历文件的迭代器
while(true) {
while(true) {
while(i$.hasNext()) {
FileStatus file = (FileStatus)i$.next();
Path path = file.getPath();
long length = file.getLen();//该文件长度
if(length != 0L) {
BlockLocation[] blkLocations;
//判断是否是本节点路径下的file
if(file instanceof LocatedFileStatus) {
blkLocations = ((LocatedFileStatus)file).getBlockLocations();//读取文件所属block的位置
} else {
FileSystem fs = path.getFileSystem(job.getConfiguration());//从别的节点上去读取文件位置
blkLocations = fs.getFileBlockLocations(file, 0L, length);
}
//判断文件是否可分割,如果是压缩的,将不可分割
if(this.isSplitable(job, path)) {
long blockSize = file.getBlockSize(); //获取当前block_size大小,默认128M
/*这里是关键,splitSize是由blockSize,minSize,maxSize共同确定的,这里的computeSplitSize()方法其具体实现方式是return Math.max(minSize, Math.min(maxSize, blockSize));也就是maxSize和blockSize的最小值与minSize的最大值就是splitSize*/
long splitSize = this.computeSplitSize(blockSize, minSize, maxSize);
long bytesRemaining;//分好片的剩余字节数
int blkIndex;
//当剩余数据分片大小与分片大小比值大于1.1时,继续分片,小于时停止分片
for(bytesRemaining = length; (double)bytesRemaining / (double)splitSize > 1.1D; bytesRemaining -= splitSize) {
blkIndex = this.getBlockIndex(blkLocations, length - bytesRemaining);
splits.add(this.makeSplit(path, length - bytesRemaining, splitSize, blkLocations[blkIndex].getHosts(), blkLocations[blkIndex].getCachedHosts()));
}
//如果余下的数据小于一个splitSize就独自是一个分片
if(bytesRemaining != 0L) {
blkIndex = this.getBlockIndex(blkLocations, length - bytesRemaining);
splits.add(this.makeSplit(path, length - bytesRemaining, bytesRemaining, blkLocations[blkIndex].getHosts(), blkLocations[blkIndex].getCachedHosts()));
}
} else {
//不可压缩就返回整块数据
splits.add(this.makeSplit(path, 0L, length, blkLocations[0].getHosts(), blkLocations[0].getCachedHosts()));
}
} else {
//对于文件长度为0时,返回一个空的split
splits.add(this.makeSplit(path, 0L, length, new String[0]));
}
}
job.getConfiguration().setLong("mapreduce.input.fileinputformat.numinputfiles", (long)files.size()); //设置输入文件数量
sw.stop();
if(LOG.isDebugEnabled()) {
LOG.debug("Total # of splits generated by getSplits: " + splits.size() + ", TimeTaken: " + sw.now(TimeUnit.MILLISECONDS));
}
return splits;
}
}
}
这里可以看出大文件分片的原理,一般可以