TextInputFormat的源码注释为:
查看InputFormat接口的源码注释我们了解到这个接口的作用为:
在InputFormat的源代码中有如下两个方法:
//获取切片(一个切片就是一个Mapper任务)数组
InputSplit[] getSplits(JobConf job, int numSplits) throws IOException;
//获取RecordReader
RecordReader<K, V> getRecordReader(InputSplit split,
JobConf job,
Reporter reporter) throws IOException;
追踪FileInputFormat接口,切片方法如下:
/** Splits files returned by {@link #listStatus(JobConf)} when they're too big.*/
public InputSplit[] getSplits(JobConf job, int numSplits)
throws IOException {
//开启线程
Stopwatch sw = new Stopwatch().start();
//获取文件状态
FileStatus[] files = listStatus(job);
// 为了度量所有文件总长度,需要设置文件输入数和每个文件长度(job传的可能是一个目录,目录下有很多文件)
job.setLong(NUM_INPUT_FILES, files.length);
long totalSize = 0; // 计算文件总大小
for (FileStatus file: files) {
if (file.isDirectory()) { // 检查有效文件
throw new IOException("Not a file: "+ file.getPath());
}
totalSize += file.getLen();
}
//默认切片是block大小(128M)如果自己定义了切片的大小就按照设置参数来,所有文件大小/切片数-->就可以求得目标切片大小
long goalSize = totalSize / (numSplits == 0 ? 1 : numSplits);
//最小的切片大小只要不动配置文件参数默认是1,如果按设置了就按设置参数来
long minSize = Math.max(job.getLong(org.apache.hadoop.mapreduce.lib.input.FileInputFormat.SPLIT_MINSIZE, 1), minSplitSize);
// 生成切片步骤
//准备一个文件切片的集合-->需要去看看文件切片的机制
ArrayList<FileSplit> splits = new ArrayList<FileSplit>(numSplits);
NetworkTopology clusterMap = new NetworkTopology();//网络拓扑,这个先不管
for (FileStatus file: files) {
//文件路径(namenode会存储这些信息)
Path path = file.getPath();
//文件长度(namenode会存储这些信息)
long length = file.getLen();
if (length != 0) {
FileSystem fs = path.getFileSystem(job);
//获取文件的block块(只会获取一次完整文件的块(找到一个活的块就不会找他的备份))
BlockLocation[] blkLocations;
if (file instanceof LocatedFileStatus) {
blkLocations = ((LocatedFileStatus) file).getBlockLocations();
} else {
blkLocations = fs.getFileBlockLocations(file, 0, length);
}
if (isSplitable(fs, path)) {
//获取block块大小(默认128M)
long blockSize = file.getBlockSize();
//计算切片大小:在(目标大小,最小大小(设置的参数),block块大小)中取中值的操作
long splitSize = computeSplitSize(goalSize, minSize, blockSize);
long bytesRemaining = length;
//SPLIT_SLOP指的是切片的溢出系数为1.1
//(每次切片时,都要判断切完剩下的部分是否大于块的 1.1倍,不大于 1.1 倍就划分一块切片)。
while (((double) bytesRemaining)/splitSize > SPLIT_SLOP) {
String[][] splitHosts = getSplitHostsAndCachedHosts(blkLocations,
length-bytesRemaining, splitSize, clusterMap);
//记录了路径,切片偏移量,切片大小等,加入文件切片的集合中
splits.add(makeSplit(path, length-bytesRemaining, splitSize,
splitHosts[0], splitHosts[1]));
//切完一次剩余的切片大小
bytesRemaining -= splitSize;
}
if (bytesRemaining != 0) {
String[][] splitHosts = getSplitHostsAndCachedHosts(blkLocations, length
- bytesRemaining, bytesRemaining, clusterMap);
splits.add(makeSplit(path, length - bytesRemaining, bytesRemaining,
splitHosts[0], splitHosts[1]));
}
} else {
String[][] splitHosts = getSplitHostsAndCachedHosts(blkLocations,0,length,clusterMap);
splits.add(makeSplit(path, 0, length, splitHosts[0], splitHosts[1]));
}
} else {
//Create empty hosts array for zero length files
splits.add(makeSplit(path, 0, length, new String[0]));
}
}
sw.stop();
if (LOG.isDebugEnabled()) {
LOG.debug("Total # of splits generated by getSplits: " + splits.size()
+ ", TimeTaken: " + sw.elapsedMillis());
}
//将集合转为数组
return splits.toArray(new FileSplit[splits.size()]);
}
追踪RecordReader接口,发现其功能为:
追踪InputSplit抽象类,查看注释发现其功能为:
因此我们知道这里切片只是一个逻辑划分,记录了一些切片信息
数据切片只是在逻辑上对输入数据进行分片,并不会在磁盘上将其切分成分片进行存储。
InputSplit 只记录了分片的元数据信息,比如起始位置、长度 以及所在的节点列表等
提交切片规划文件到Yarn上,Yarn上的 MrAppMaster 就可以根据切片规划文件计算开启 map task 个数
在map task的read阶段接受输入分片,根据RecordReader按照InputSplit 记录 的位置信息读取数据,会将一个分片处理成一行一行的数据即一个键值对键为行偏移量,值为文本数据
'shuffle是MapReduce处理流程中的一个过程,他的每一个处理步骤是分散在各个map任务和reduce任务上完成的.整体看,有三个操作'
1)分区
2)Sort根据key排序
3)Combiner进行局部合并
注意:
reduce task 的数量的决定是可以直接手动设置的并不一定和map task 数量相等,默认是1
如果分区数不是 1,但是 reducetask 为 1,是不执行分区操作的(执行分区之前会进行判断)