Hadoop的数据输入的源码解析
我们知道,任何一个工程项目,最重要的是三个部分:输入,中间处理,输出。今天我们来深入的了解一下我们熟知的Hadoop系统中,输入是如何输入的?
在hadoop中,输入数据都是通过对应的InputFormat类和RecordReader类来实现的,其中InputFormat来实现将对应输入文件进行分片,RecordReader类将对应分片中的数据读取进来。具体的方式如下:
(1)InputFormat类是一个接口。
public interface InputFormat<K, V> {
InputSplit[] getSplits(JobConf job, int numSplits) throws IOException;
RecordReader<K, V> getRecordReader(InputSplit split,
JobConf job,
Reporter reporter) throws IOException;
}
(2)FileInputFormat类实现了InputFormat接口。该类实现了getSplits方法,但是它也没有实现对应的getRecordReader方法。也就是说FileInputFormat还是一个抽象类。这里需要说明的一个问题是,FileInputFormat用isSplitable方法来指定对应的文件是否支持数据的切分。默认情况下都是支持的,一般子类都需要重新实现它。
public abstract class FileInputFormat<K, V> implements InputFormat<K, V> {
public InputSplit[] getSplits(JobConf job, int numSplits) throws IOException {
FileStatus[] files = listStatus(job);
// Save the number of input files in the job-conf
job.setLong(NUM_INPUT_FILES, files.length);
long totalSize = 0; // compute total size
for (FileStatus file: files) { // check we have valid files
if (file.isDir()) {
throw new IOException("Not a file: "+ file.getPath());
}
totalSize += file.getLen();
}
long goalSize = totalSize / (numSplits == 0 ? 1 : numSplits);
long minSize = Math.max(job.getLong("mapred.min.split.size", 1),
minSplitSize);
// generate splits
ArrayList<FileSplit> splits = new ArrayList<FileSplit>(numSplits);
NetworkTopology clusterMap = new NetworkTopology();
for (FileStatus file: files) {
Path path = file.getPath();
FileSystem fs = path.getFileSystem(job);
long length = file.getLen();
BlockLocation[] blkLocations = fs.getFileBlockLocations(file, 0, length);
if ((length != 0) && isSplitable(fs, path)) {
long blockSize = file.getBlockSize();
long splitSize = computeSplitSize(goalSize, minSize, blockSize);
long bytesRemaining = length;
while (((double) bytesRemaining)/splitSize > SPLIT_SLOP) {
String[] splitHosts = getSplitHosts(blkLocations,
length-bytesRemaining, splitSize, clusterMap);
splits.add(new FileSplit(path, length-bytesRemaining, splitSize,
splitHosts));
bytesRemaining -= splitSize;
}
if (bytesRemaining != 0) {
splits.add(new FileSplit(path, length-bytesRemaining, bytesRemaining,
blkLocations[blkLocations.length-1].getHosts()));
}
} else if (length != 0) {
String[] splitHosts = getSplitHosts(blkLocations,0,length,clusterMap);
splits.add(new FileSplit(path, 0, length, splitHosts));
} else {
//Create empty hosts array for zero length files
splits.add(new FileSplit(path, 0, length, new String[0]));
}
}
LOG.debug("Total # of splits: " + splits.size());
return splits.toArray(new FileSplit[splits.size()]);
}
//该方法是用来判断是否可以进行数据的切分
protected boolean isSplitable(FileSystem fs, Path filename) {
return true;
}
//但是它也没有实现对应的getRecordReader方法。也就是说FileInputFormat还是一个抽象类。
public abstract RecordReader<K, V> getRecordReader(InputSplit split,
JobConf job,
Reporter reporter) throws IOException;
}
(3)TextFileInputFormat类仅仅实现了FileInputFormat类的getRecordReader方法,并且重写了isSplitable方法,他并没有实现getSplits方法,由此可知,他的getSplits的实现还是交由父类FileInputFormat来实现的。(这里需要注意TextFileInputFormat并不是InputFormat的子类,TextFileInputFormat它仅仅是继承了InputFormat的getRecordReader的方法而已。)
public class TextInputFormat extends FileInputFormat<LongWritable, Text>
implements JobConfigurable {
private CompressionCodecFactory compressionCodecs = null;
public void configure(JobConf conf) {
compressionCodecs = new CompressionCodecFactory(conf);
}
//子类重新实现了isSplitable方法
protected boolean isSplitable(FileSystem fs, Path file) {
final CompressionCodec codec = compressionCodecs.getCodec(file);
if (null == codec) {
return true;
}
return codec instanceof SplittableCompressionCodec;
}
//该方法实现了将文件中的数据读入到对应的Map方法中。
public RecordReader<LongWritable, Text> getRecordReader(
InputSplit genericSplit, JobConf job,
Reporter reporter)
throws IOException {
reporter.setStatus(genericSplit.toString());
String delimiter = job.get("textinputformat.record.delimiter");
byte[] recordDelimiterBytes = null;
if (null != delimiter) recordDelimiterBytes = delimiter.getBytes();
return new LineRecordReader(job, (FileSplit) genericSplit,
recordDelimiterBytes);
}
}
从上面可以看出一个Text格式的文件是通过什么样的类继承层次输入到map方法中。下面主要介绍一下,到底是如何切分的?我们从类的继承层次关系上可以看出,具体的切分方式是通过FileInputFormat类来实现的。因此,要了解文件是如何切分的,只需要查看一下FileInputFormat类中的getSplits方法的实现细节即可。下面我再次把FileInputFormat类中的getSplits方法贴出来:然后分析每一句代码。
public InputSplit[] getSplits(JobConf job, int numSplits)
throws IOException {
FileStatus[] files = listStatus(job); //列出当前job中所有的输入文件
// Save the number of input files in the job-conf
job.setLong(NUM_INPUT_FILES, files.length); //设置当前job的输入文件数目
//计算当前job所有输入文件总的大小
long totalSize = 0; // compute total size
//遍历每一个文件
for (FileStatus file: files) { // check we have valid files
if (file.isDir()) {
throw new IOException("Not a file: "+ file.getPath());
}
totalSize += file.getLen();
}
// numSplits是分片数,goalSize是平均每一个分片的大小,minSize是每个分片最小值
long goalSize = totalSize / (numSplits == 0 ? 1 : numSplits);
long minSize = Math.max(job.getLong("mapred.min.split.size", 1),
minSplitSize);
// generate splits 计算分片
ArrayList<FileSplit> splits = new ArrayList<FileSplit>(numSplits);
NetworkTopology clusterMap = new NetworkTopology();
for (FileStatus file: files) {
Path path = file.getPath();
FileSystem fs = path.getFileSystem(job);
long length = file.getLen();
//获取文件的位置
BlockLocation[] blkLocations = fs.getFileBlockLocations(file, 0, length);
//isSplitable方法根据对应文件名称判断对应文件是否可以切分
if ((length != 0) && isSplitable(fs, path)) {
long blockSize = file.getBlockSize();//获取文件块的大小
// computeSplitSize方法计算真正的分片大小
long splitSize = computeSplitSize(goalSize, minSize, blockSize);
long bytesRemaining = length;//文件剩余大小
// SPLIT_SLOP=1.1,文件大小/分片的大小> SPLIT_SLOP则进行切分。
while (((double) bytesRemaining)/splitSize > SPLIT_SLOP) {
// splitHosts用来记录分片元数据信息(包括切片的位置,大小等等)
String[] splitHosts = getSplitHosts(blkLocations,
length-bytesRemaining, splitSize, clusterMap);
splits.add(new FileSplit(path, length-bytesRemaining, splitSize,
splitHosts));
bytesRemaining -= splitSize;
}
if (bytesRemaining != 0) {
splits.add(new FileSplit(path, length-bytesRemaining, bytesRemaining,
blkLocations[blkLocations.length-1].getHosts()));
}
} else if (length != 0) { //如果文件不能切分,相应的会将整个文件作为一个分片。
String[] splitHosts = getSplitHosts(blkLocations,0,length,clusterMap);
splits.add(new FileSplit(path, 0, length, splitHosts));
} else {
//Create empty hosts array for zero length files
splits.add(new FileSplit(path, 0, length, new String[0]));
}
}
LOG.debug("Total # of splits: " + splits.size());
return splits.toArray(new FileSplit[splits.size()]);
}
//真正计算分片大小的地方。
protected long computeSplitSize(long goalSize, long minSize,
long blockSize) {
return Math.max(minSize, Math.min(goalSize, blockSize));
}
综上所述,对于MR的输入文件采用的方式是通过FileInputFormat类来进行数据的切分,在切分之前,是通过isSplitable方法来判断是否可以切分,若不能切分,则会将整个文件作为一个分片作为输入。因此,若有业务需求需要对应文件不能进行切分的话,可以将isSplitable方法方位false即可。
这里还需要注意一个问题,倘若你的文件都是小文件的话,对应的getSplits方法也不会对其进行切分的。一般情况小文件指的是其大小小于对应hadoop中HDFS的块的大小(128M)。
Hadoop的数据输入的源码解析的更多相关文章
- 【大数据】深入源码解析Map Reduce的架构
这几天学习了MapReduce,我参照资料,自己又画了两张MapReduce的架构图. 这里我根据架构图以及对应的源码,来解释一次分布式MapReduce的计算到底是怎么工作的. 话不多说,开始! ...
- Java ThreadLocal 的使用与源码解析
GitHub Page: http://blog.cloudli.top/posts/Java-ThreadLocal-的使用与源码解析/ ThreadLocal 主要解决的是每个线程绑定自己的值,可 ...
- 谷歌BERT预训练源码解析(一):训练数据生成
目录预训练源码结构简介输入输出源码解析参数主函数创建训练实例下一句预测&实例生成随机遮蔽输出结果一览预训练源码结构简介关于BERT,简单来说,它是一个基于Transformer架构,结合遮蔽词 ...
- [源码解析] PyTorch 分布式(2) --- 数据加载之DataLoader
[源码解析] PyTorch 分布式(2) --- 数据加载之DataLoader 目录 [源码解析] PyTorch 分布式(2) --- 数据加载之DataLoader 0x00 摘要 0x01 ...
- [源码解析] PyTorch 流水线并行实现 (3)--切分数据和运行时系统
[源码解析] PyTorch 流水线并行实现 (3)--切分数据和运行时系统 目录 [源码解析] PyTorch 流水线并行实现 (3)--切分数据和运行时系统 0x00 摘要 0x01 分割小批次 ...
- HDFS源码解析:教你用HDFS客户端写数据
摘要:终于开始了这个很感兴趣但是一直觉得困难重重的源码解析工作,也算是一个好的开端. 本文分享自华为云社区<hdfs源码解析之客户端写数据>,作者: dayu_dls. 在我们客户端写数据 ...
- Fresco源码解析 - DataSource怎样存储数据
Fresco源码解析 - DataSource怎样存储数据 datasource是一个独立的 package,与FB导入的guava包都在同一个工程内 - fbcore. datasource的类关系 ...
- [源码]解析 SynchronousQueue 上界,下界.. 数据保存和数据传递. 堵塞队列. 有无频繁await?
简析SynchronousQueue.LinkedBlockingQueue(两个locker,更快),ArrayBlockingQueue(一个locker,读写都竞争) 三者都是bloc ...
- Vue源码解析---数据的双向绑定
本文主要抽离Vue源码中数据双向绑定的核心代码,解析Vue是如何实现数据的双向绑定 核心思想是ES5的Object.defineProperty()和发布-订阅模式 整体结构 改造Vue实例中的dat ...
随机推荐
- CSS3的基础用法
CSS3的现状:1.浏览器支持程度差,需要添加私有前缀 2.移动端支持优于PC端 3.不断改进中 4.应用相对广泛 在CSS3中增加新增了许多灵活查找元素的方法,极大的提高了查找元素的效率和精准度.C ...
- [官方作品] 关于ES4的设首页问题
[官方作品] 关于ES4的设首页问题 Skyfree 发表于 2013-2-10 21:55:03 https://www.itsk.com/thread-254503-1-1.html 关于ES4设 ...
- SQL SERVER 2008 评估期已过
开始-->运行-->regedit 修改注册表: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\100\Config ...
- Snoopy+phpquery采集demo
用phpquery类,写了个采集的demo,以 某网贷平台的 一个列表为例,我们要采集该平台下面的 各平台名称,结构树如下 include 'phpQuery.php'; phpQuery::newD ...
- NodeJS中的异步I/O、事件驱动
nodejs的主要特点是单线程.异步I/O.事件驱动.让我们先大概了解一下这些名词的意思. 单线程 单线程是任务按照顺序执行的,并且每次只执行一个任务,只有前面的任务执行完成以后,后面的任务才执行.在 ...
- Java中的方法应用
一.如何定义java中的方法 所谓方法,就是用来解决一类问题的代码的有序组合,是一个功能模块. 语法: 1. 访问修饰符:方法允许被访问的权限范围, 可以是 public.protected.priv ...
- TensorFlow 在android上的Demo(1)
转载时请注明出处: 修雨轩陈 系统环境说明: ------------------------------------ 操作系统 : ubunt 14.03 _ x86_64 操作系统 内存: 8GB ...
- python 执行execute遇到的问题
1.如下方式去查询无法查询出结果,但直接在数据库查询中去查询是能查询到结果的,郁闷中,花了很久的时间才知道原来是双引号导致的 把:name="%s" 中的%s前后的双引号去掉就对了 ...
- ios基础篇(二十九)—— 多线程(Thread、Cocoa operations和GCD)
一.进程与线程 1.进程 进程是指在系统中正在运行的一个应用程序,每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内: 如果我们把CPU比作一个工厂,那么进程就好比工厂的车间,一个工厂有 ...
- 删除ORACLE的步骤
1.关闭oracle所有的服务.可以在windows的服务管理器中关闭: 2.打开注册表:regedit 打开路径: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlS ...