Hadoop中的InputFormat解析

1、InputFormat

      InputFormat是Hadoop平台上Mapreduce输入的规范,仅有两个抽象方法。

  • List<InputSplit> getSplits(), 获取由输入文件计算出输入分片(InputSplit),解决数据或文件分割成片问题。
  • RecordReader<K,V> createRecordReader(),创建RecordReader,从InputSplit中读取数据,解决读取分片中数据问题。

   InputFormat主要能完成下列工作:

       1、Validate the input-specification of the job.  (首先验证作业的输入的正确性)

       2、 Split-up the input file(s) into logical InputSplits, each of which is then assigned to an individual Mapper.(将输入的文件划分成一系列逻辑分片 

           (InputSplit),一个InputSplit将会被分配给一个独立的MapTask )

       3、Provide the RecordReader implementation to be used to glean input records from the logical InputSplit for processing by the Mapper.(提供RecordReader实  

           现,读取InputSplit中的“K-V对”供Mapper使用)

 InputFormat的源代码:

  

public abstract class InputFormat<K, V> {

  /** 
   * 每个InputSplit的分片被分配到一个独立的Mapper上
   * 注:1、这个分片是逻辑上对输入数据进行分片,而实际上输入文件没有被切割成一个个小块。
   *     每个分片由输入文件的路径,起始位置,偏移量等
   *     2、在InputFormat中创建的RecordReader也要使用InputSplit
   */
  public abstract 
    List<InputSplit> getSplits(JobContext context ) throws IOException, InterruptedException;

  /**
   * 为每个分片创建一个record reader
   */
  public abstract 
    RecordReader<K,V> createRecordReader(InputSplit split,TaskAttemptContext context) 
            throws IOException, InterruptedException;

2.InputSplit

     Mapper输入的是一个个分片,称InputSplit。在这个抽象类中,是将每个输入分片(Split)中的内容解析成K-V值。

    InputSplit的源代码:

public abstract class InputSplit {
  /**
   * 得到每个分片的大小,可以按照分片大小排序。
   */
  public abstract long getLength() throws IOException, InterruptedException;

  /**
   * Get the list of nodes by name where the data for the split would be local.
   * The locations do not need to be serialized.
   * 获取存储该分片的数据所在的节点位置
   */
  public abstract String[] getLocations() throws IOException, InterruptedException;
}

2.1 下面看看InputSplit的一个子类,FileSplit类:

 1  public FileSplit() {}
 2 
 3   /** Constructs a split with host information
 4    *
 5    * @param file the file name
 6    * @param start the position of the first byte in the file to process
 7    * @param length the number of bytes in the file to process
 8    * @param hosts the list of hosts containing the block, possibly null
 9    */
10   public FileSplit(Path file, long start, long length, String[] hosts) {
11     this.file = file;
12     this.start = start;
13     this.length = length;
14     this.hosts = hosts;
15   }
16  
17   /** The file containing this split's data. */
18   public Path getPath() { return file; }
19   
20   /** The position of the first byte in the file to process. */
21   public long getStart() { return start; }
22   
23   /** The number of bytes in the file to process. */
24   @Override
25   public long getLength() { return length; }
26 
27   @Override
28   public String toString() { return file + ":" + start + "+" + length; }
29 
30   ////////////////////////////////////////////
31   // Writable methods
32   ////////////////////////////////////////////
33 
34   @Override
35   public void write(DataOutput out) throws IOException {
36     Text.writeString(out, file.toString());
37     out.writeLong(start);
38     out.writeLong(length);
39   }
40 
41   @Override
42   public void readFields(DataInput in) throws IOException {
43     file = new Path(Text.readString(in));
44     start = in.readLong();
45     length = in.readLong();
46     hosts = null;
47   }
48 
49   @Override
50   public String[] getLocations() throws IOException {
51     if (this.hosts == null) {
52       return new String[]{};
53     } else {
54       return this.hosts;
55     }
56

从源码中可以看出,FileSplit有四个属性:文件路径,分片起始位置,分片长度和存储分片的hosts。用这四项数据,就可以计算出提供给每个Mapper的分片数据。在InputFormat的getSplit()方法中构造分片,分片的四个属性会通过调用FileSplit的Constructor设置。

2.2再看一个InputSplit的子类:CombineFileSplit。源码如下:

为什么介绍该类呢,因为该类对小文件的处理是很有效的,所有深入理解该类,将有助于该节学习。

上面我们介绍的FileSplit对应的是一个输入文件,也就是说,如果用FileSplit对应的FileInputFormat作为输入格式,那么即使文件特别小,也是作为一个单独的InputSplit来处理,而每一个InputSplit将会由一个独立的Mapper Task来处理。在输入数据是由大量小文件组成的情形下,就会有同样大量的InputSplit,从而需要同样大量的Mapper来处理,大量的Mapper Task创建销毁开销将是巨大的,甚至对集群来说,是灾难性的!

CombineFileSplit是针对小文件的分片,它将一系列小文件封装在一个InputSplit内,这样一个Mapper就可以处理多个小文件。可以有效的降低进程开销。与FileSplit类似,CombineFileSplit同样包含文件路径,分片起始位置,分片大小和分片数据所在的host列表四个属性,只不过这些属性不再是一个值,而是一个列表。

需要注意的一点是,CombineFileSplit的getLength()方法,返回的是这一系列数据的数据的总长度。

现在,我们已深入的了解了InputSplit的概念,看了其源码,知道了其属性。我们知道数据分片是在InputFormat中实现的,接下来,我们就深入InputFormat的一个子类,FileInputFormat看看分片是如何进行的。

3 、FileInputFormat

FileInputFormat中,分片方法代码及详细注释如下,就不再详细解释该方法:

原文地址:https://www.cnblogs.com/rolly-yan/p/3702018.html