1、block:block是物理切块,在文件上传到HDFS文件系统后,对大文将以每128MB的大小切分若干,存放在不同的DataNode上;
2、split:split是逻辑切片,在mapreduce中的map task开始之前,将文件按照指定的大小切割成若干个部分,每一部分称为一个split,默认是split的大小与block的大小相等,均为128MB。
注意:在hadoop1.x版本中,block默认的大小为64MB,在hadoop2.x版本修改成了128MB。
1、block默认配置在hdfs-default.xml中(hadoop-hdfs-x.x.x.jar目录下)
dfs.blocksize
134217728
默认block的大小参数配置以字节为单位(例如134217728,128 MB)
也可以使用如128k,512m,1g等为单位(不区分大小写)
注意:默认配置就是最佳实践
2、split大小由minSize、minSize、blocksize决定,以下是默认配置情况下的
long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job));
protected long getFormatMinSplitSize() {
return 1;
}
public static long getMinSplitSize(JobContext job) {
// SPLIT_MINSIZE默认值为0,因此getMinSplitSize(JobContext job)返回值为1
return job.getConfiguration().getLong(SPLIT_MINSIZE, 1L);
}
long maxSize = getMaxSplitSize(job);
// 返回默认值MAX_VALUE
public static long getMaxSplitSize(JobContext context) {
return context.getConfiguration().getLong(SPLIT_MAXSIZE,
Long.MAX_VALUE);
}
// 获取配置文件参数值
public long getLong(String name, long defaultValue) {
// 用户为自定义参数属性
String valueString = getTrimmed(name);
if (valueString == null)
// 返回系统默认值,即public static final long MAX_VALUE
return defaultValue;
String hexString = getHexDigits(valueString);
if (hexString != null) {
return Long.parseLong(hexString, 16);
}
return Long.parseLong(valueString);
}
由上面三个参数就可以计算出分片大小了,也能得到如下结论:
在mapreduce的FileInputFormat类中的getSplits() 方法对文件进行split,算法如下:
Math.max(minSize,Math.min(maxSize, blockSize)),其中maxSize是取得longValueMax的值
1.如果blockSize小于maxSize && blockSize 大于 minSize之间,那么split就是blockSize(一对一);
2.如果blockSize小于maxSize && blockSize 小于 minSize之间,那么split就是minSize;
3.如果blockSize大于maxSize && maxSize 大于 minSize之间,那么split就是maxSize(多对一);
4.如果blockSize大于maxSize && maxSize 小于 minSize之间,那么split就是maxSize(不存在这种关系)。
文件大小不变,minsize大小默认,增加map task数量,减小maxSize,则split减小
文件大小不变,maxSize大小默认,减小map task数量,增大minSize,则split增大
注意:split大小如何调整,split只能是一个文件的分片,不能让多个小文件“划入”一个split中
文件的最后一个分片可能会超过128MB,由于常量SPLIT_SLOP = 1.1决定,大小范围在:0MB < lastSplit < 128+12.8 MB
/**
* 生成切片对象集合,InputSplit对象封装.
* @param job the job context
* @throws IOException
*/
public List getSplits(JobContext job) throws IOException {
StopWatch sw = new StopWatch().start();
// 分片最小值,getFormatMinSplitSize()返回值为1,getMinSplitSize(job)返回值为1
long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job));
// 分片最大值是常量:public static final long MAX_VALUE
long maxSize = getMaxSplitSize(job);
// 生成分片列表
List splits = new ArrayList();
// 判断文件是否可用
List files = listStatus(job);
// 获取文件路径、大小
for (FileStatus file: files) {
Path path = file.getPath();
long length = file.getLen();
if (length != 0) {
BlockLocation[] blkLocations;
// 获取block位置信息
if (file instanceof LocatedFileStatus) {
blkLocations = ((LocatedFileStatus) file).getBlockLocations();
} else {
FileSystem fs = path.getFileSystem(job.getConfiguration());
blkLocations = fs.getFileBlockLocations(file, 0, length);
}
// 判断文件是否可切片
if (isSplitable(job, path)) {
// 获取block大小,默认128MB
long blockSize = file.getBlockSize();
// 计算split大小,默认128MB
long splitSize = computeSplitSize(blockSize, minSize, maxSize);
// 文件大小
long bytesRemaining = length;
// 当文件大小满足bytesRemaining/128 > 1.1 进行切片
// private static final double SPLIT_SLOP = 1.1;
while (((double) bytesRemaining)/splitSize > SPLIT_SLOP) {
// 记录block索引位置
int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);
splits.add(makeSplit(path, length-bytesRemaining, splitSize,
blkLocations[blkIndex].getHosts(),
blkLocations[blkIndex].getCachedHosts()));
// 文件剩余大小
bytesRemaining -= splitSize;
}
// 判断文件是否切片完成
if (bytesRemaining != 0) {
int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);
splits.add(makeSplit(path, length-bytesRemaining, bytesRemaining,
blkLocations[blkIndex].getHosts(),
blkLocations[blkIndex].getCachedHosts()));
}
} else { // not splitable
splits.add(makeSplit(path, 0, length, blkLocations[0].getHosts(),
blkLocations[0].getCachedHosts()));
}
} else { // 文件大小不满足切片要求
//Create empty hosts array for zero length files
splits.add(makeSplit(path, 0, length, new String[0]));
}
}
// Save the number of input files for metrics/loadgen
job.getConfiguration().setLong(NUM_INPUT_FILES, files.size());
sw.stop();
if (LOG.isDebugEnabled()) {
LOG.debug("Total # of splits generated by getSplits: " + splits.size()
+ ", TimeTaken: " + sw.now(TimeUnit.MILLISECONDS));
}
return splits;
}
// 计算实际分片大小
protected long computeSplitSize(long blockSize, long minSize,
long maxSize) {
return Math.max(minSize, Math.min(maxSize, blockSize));
}
// 封装分片信息,文件路径,开始位置,文件大小,主机名,存储该block的所有主机名列表
protected FileSplit makeSplit(Path file, long start, long length, String[] hosts,
String[] inMemoryHosts) {
return new FileSplit(file, start, length, hosts, inMemoryHosts);
}