MapReduce之InputFormat理解

一 InputFormat主要作用:

#验证job的输入规范

#对输入的文件进行切分,形成多个InputSplit文件,每一个InputSplit对应着一个map任务

#创建RecordReader,从InputSplit分片中读取数据供map使用

 

二 有几个比较重要的实现

2.1FileInputFormat: 主要用于处理文件的一个InputFormat类,它包括子类:

2.1.1 FiexedLengthInputFormat: 读取输入文件的固定长度的记录,这种文件不该是文本文件,二进制文件比较多

2.1.2KeyValueInputFormat: 用于读取普通文本文件,文件按照行分割,每一行由key和value组成,key 和 value的分隔符若没有指定,那么整行为key,value为空

2.1.3TextInputFormat: 文件按照行划分,key就是这一行在文件中的偏移量,value就是这一行文本

2.1.5SequenceFileInputFormat:

=>SequenceFileAsBinaryInputFormat:InputFormat从sequenceFile读取以二进制格式读取key和value. 他只能读取SequenceFile

 

=>SequenceFileAsTextInputFormat:以文本格式读取

2.1.6NLineInputFormat: 是可以将N行数据划分为一个Split,作为MapTask输入

2.2DBInputFormat: 主要用于处理数据库数据的InputFormat类

2.3CombineFileInputFormat:

我们知道文件切分的时候,FileInputFormat默认情况下,如果一个文件小于blockSize,那么这个文件就是一个InputSplits, 如果大于blockSize,则是先按照文件大小/blockSize,如果有剩余单独成为一个InputSplit。

 

但是如果小文件太多,那么就会生成的切片太多,从而导致Map任务增多,Map任务增多所需资源就多。所以CombineFileInputFormat通过合并多个小文件成为一个分片,分片过程也考虑同一节点,同一机架的数据本地性,让每一个Mapper任务可以处理更多的数据

 

三 CombineFileInputFormat详解

我们知道,InputFormat接口,需要我们自己提供RecordReader.

 

它的原理就是:

我们通常大文件都是使用FileSplit,但是现在是包含多个文件的split,我们需要使用CombineFileSplit。它会将输入多个数据文件(小文件)元数据全部包装到CombineFileSplit里面。因为小文件都是单独的一个Block文件,一个CombineFileSplit包含一组文件的Block信息,涉及到每个文件的偏移,长度,block位置等元数据.

在执行Map-Reduce的时候,需要读取文本数据,那么对于Combine

FileSplit,你需要处理其包含的小文件block,就要对应设置RecordReader,才能正确读取文件数据内容。

 

编程流程:

#实现一个RecordReader来读取CombineFileSplit包装的Block

#继承CombineFileInputFormat实现一个我们自定义的RecordReader的类,用来读取每个文件的数据

 

public class MyCombineFileRecordReader extends RecordReader {

      private CombineFileSplit combineFileSplit;//封装了小文件的meta信息,诸如长度,offset以及块的信息

      private LineRecordReader lineRecordReader = new LineRecordReader();//用于读取行数据

      private Path[] paths; //小文件的输入路径

      private int length;

      private int currentIndex;

      private float currentProgress = 0;

      private LongWritable currentKey;

      private BytesWritable currentValue = new BytesWritable();

     

     

      public MyCombineFileRecordReader(CombineFileSplit combineFileSplit, TaskAttemptContext context, Integer index){

           super();

           this.combineFileSplit = combineFileSplit;

           //当前要处理的小文件block在CombineFileSplit中的索引

           this.currentIndex = index;

      }

     

      @Override

      public void initialize(InputSplit split, TaskAttemptContext context) throws IOException, InterruptedException {

           this.combineFileSplit = (CombineFileSplit) split;

           this.paths = combineFileSplit.getPaths();

           this.length = paths.length;

           // 处理CombineFileSplit中的一个小文件Block,因为使用LineRecordReader,

           //需要构造一个FileSplit对象,然后才能够读取数据

           //当前小文件的路径

           Path path = combineFileSplit.getPath(currentIndex);

           //当前小文件的偏移量

           long start = combineFileSplit.getOffset(currentIndex);

           //当前小文件的长度

           long length = combineFileSplit.getLength(currentIndex);

           //小文件block所在的节点信息

           String[] hosts = combineFileSplit.getLocations();

           FileSplit fileSplit = new FileSplit(path, start, length, hosts);

           lineRecordReader.initialize(fileSplit, context);

           context.getConfiguration().set("map.input.file.name", path.getName());

      }

 

      @Override

      public boolean nextKeyValue() throws IOException, InterruptedException {

 

           return currentIndex >= 0&& currentIndex < length && lineRecordReader.nextKeyValue();

      }

 

      @Override

      public LongWritable getCurrentKey() throws IOException, InterruptedException {

 

           return lineRecordReader.getCurrentKey();

      }

 

      @Override

      public BytesWritable getCurrentValue() throws IOException, InterruptedException {

           byte[] contents = lineRecordReader.getCurrentValue().getBytes();

           int len = contents.length;

           currentValue.set(contents, 0, len);

           return currentValue;

      }

 

      @Override

      public float getProgress() throws IOException, InterruptedException {

           if (currentIndex >= 0&& currentIndex < length) {

                 currentProgress = (float) currentIndex / length;

                 return currentProgress;

           }

           return currentProgress;

      }

 

      @Override

      public void close() throws IOException {

           lineRecordReader.close();

      }

}

 

public class MyCombineFileInputFormat extends CombineFileInputFormat {

      @Override

      public RecordReader createRecordReader(InputSplit split, TaskAttemptContext context) throws IOException {

           CombineFileSplit combineFileSplit = (CombineFileSplit) split;

           CombineFileRecordReader recordReader =

                 new CombineFileRecordReader(combineFileSplit, context, MyCombineFileRecordReader.class);

           try {

                 recordReader.initialize(combineFileSplit, context);

           } catch (InterruptedException e) {

                 // TODO Auto-generated catch block

                 e.printStackTrace();

           }

           return recordReader;

      }

}

 

 

你可能感兴趣的:(大数据/Hadoop)