1.概念
1.切片是什么?
每个MapTask处理的数据单元,就叫片,也就是说,MapTask与片是一对一的关系。
2.切片大小如何设置?
切片大小默认与块大小一致
3.切片大小定义的理由
反证法:假设,我们不使用块大小来定义, 设当前块大小为128M, 片大小为100M。我们需要计算HDFS中的数据,要从里面取300M的数据。 首先,第一个块取出来,切, 还剩28M,取第二个块,切72M, 然后第二块还剩56M 再取第三块,再取44M。这样的结果,显然就是将数据块的数据分割重组,这样大大增加了资源的消耗。不论是比当前块大,还是比当前块小,都避免不了数据块的分割重组,所以,与块大小一致是目前最优。
2.代码分析
这里分成两步:
一: 获取数据并存储及配置
二: 执行切片 判断合法性等都在第二步中了
public List<InputSplit> getSplits(JobContext job) throws IOException {
// 1.准备工作 获取数据,并配置相关过滤配置
// 1.1 计时器开启
StopWatch sw = (new StopWatch()).start();
// 1.2 配置尺寸
// 1.2.1 配置最小尺寸 getFormatMinSplitSize是1
// getMinSplitSize(job) 是从配置文件中读取如果不存在,默认也是1
// mapreduce.input.fileinputformat.split.minsize可以修改配置文件修改
long minSize = Math.max(this.getFormatMinSplitSize(), getMinSplitSize(job));
// 1.2.2 配置最大尺寸
// mapreduce.input.fileinputformat.split.maxsize Long.MAX_VALUE
// 可以通过修改配置文件修改 默认是Long最大值
long maxSize = getMaxSplitSize(job);
// 1.3 获取元数据文件
List<InputSplit> splits = new ArrayList();
List<FileStatus> files = this.listStatus(job);
// 1.4 文件判断符,是否需要掠过文件夹
// 参数一:getInputDirRecursive(job): 默认false mapreduce.input.fileinputformat.input.dir.recursive
// 参数二:默认false
//可以修改mapreduce.input.fileinputformat.input.dir.nonrecursi为true修改
// 也就是说主要取决于第二个配置
boolean ignoreDirs = !getInputDirRecursive(job) && job.getConfiguration().getBoolean("mapreduce.input.fileinputformat.input.dir.nonrecursive.ignore.subdirs", false);
// 2. 数据处理阶段(这里将校验和处理放在了一起)
Iterator var10 = files.iterator();
while(true) {
while(true) {
while(true) {
FileStatus file;
// 2.1 对文件夹的处理 将元数据全部过一遍,怎么空了就出去了
do {
if (!var10.hasNext()) {
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;
}
file = (FileStatus)var10.next();
} while(ignoreDirs && file.isDirectory());
// 2.2 根据数据获取路径
Path path = file.getPath();
long length = file.getLen();
// 2.3 数据存在,开始处理数据
if (length != 0L) {
BlockLocation[] blkLocations;
// 获取数据位置
if (file instanceof LocatedFileStatus) {
blkLocations = ((LocatedFileStatus)file).getBlockLocations();
} else {
FileSystem fs = path.getFileSystem(job.getConfiguration());
blkLocations = fs.getFileBlockLocations(file, 0L, length);
}
//判断是否可以切 压缩文件不可以切,不可以切就直接当成一个片
if (this.isSplitable(job, path)) {
// 获取数据块大小和切片大小
// 这里片大小是和最小尺寸最大尺寸相关 片尺寸默认为了1 因为如果是压缩文件,切片就需要解压缩,这样太过麻烦
// 修改片大小 改大 将minSize改大 改小 将maxSize改小 也就是,反着改
// Math.max(minSize, Math.min(maxSize, blockSize));
long blockSize = file.getBlockSize();
long splitSize = this.computeSplitSize(blockSize, minSize, maxSize);
long bytesRemaining; // 剩余大小
int blkIndex;
// 开始切片
// 只要大于1.1倍就可以切, 也就是,最后一块允许超出一点的,但不允许超出0.1 防止小文件出现
for(bytesRemaining = length; (double)bytesRemaining / (double)splitSize > 1.1; bytesRemaining -= splitSize) {
blkIndex = this.getBlockIndex(blkLocations, length - bytesRemaining);
splits.add(this.makeSplit(path, length - bytesRemaining, splitSize, blkLocations[blkIndex].getHosts(), blkLocations[blkIndex].getCachedHosts()));
}
// 最后一块收尾
if (bytesRemaining != 0L) {
blkIndex = this.getBlockIndex(blkLocations, length - bytesRemaining);
splits.add(this.makeSplit(path, length - bytesRemaining, bytesRemaining, blkLocations[blkIndex].getHosts(), blkLocations[blkIndex].getCachedHosts()));
}
} else {
if (LOG.isDebugEnabled() && length > Math.min(file.getBlockSize(), minSize)) {
LOG.debug("File is not splittable so no parallelization is possible: " + file.getPath());
}
splits.add(this.makeSplit(path, 0L, length, blkLocations[0].getHosts(), blkLocations[0].getCachedHosts()));
}
} else {
splits.add(this.makeSplit(path, 0L, length, new String[0]));
}
}
}
}
}
```java
3.总结
三步走
1.配置 获取元数据,配置相关数据
- 1.配置最小片,最大片大小,
- 2.配置是否需要忽略文件夹
- 3.获取元数据
2.验证合法性(过滤)
- 1.判断操作文件是否是文件夹并执行操作
- 2.非空判断并获取数据块位置
- 3.判断是否可切分,是压缩文件直接就按整片处理
- 3.1不是压缩文件就切片
- 3.2 切片注意大小
- 3.3 最后收尾将最后一块处理掉(可超过不到0.1)