NLineInputFormat的InputSplit计算的效率

时间:2014-08-16 07:12:22

标签: java hadoop input-split

我研究了NLineInputFormat的getSplitsForFile()fn。我发现为输入文件&创建了一个InputStream。然后每n行创建一次迭代和分割。 它有效吗?特别是当在启动映射器任务之前在1个节点上发生此读取操作时。如果1有5GB的文件怎么办?基本上它意味着文件数据被寻找两次,一次是在分割创建过程中。从mapper任务中读取一次。                    如果这是一个瓶颈,hadoop的工作如何覆盖这个?

 public static List<FileSplit> getSplitsForFile(FileStatus status,
          Configuration conf, int numLinesPerSplit) throws IOException {
        List<FileSplit> splits = new ArrayList<FileSplit> ();
        Path fileName = status.getPath();
        if (status.isDirectory()) {
          throw new IOException("Not a file: " + fileName);
        }
        FileSystem  fs = fileName.getFileSystem(conf);
        LineReader lr = null;
        try {
          FSDataInputStream in  = fs.open(fileName);
          lr = new LineReader(in, conf);
          Text line = new Text();
          int numLines = 0;
          long begin = 0;
          long length = 0;
          int num = -1;
<!-- my part of concern start -->
          while ((num = lr.readLine(line)) > 0) {
            numLines++;
            length += num;
            if (numLines == numLinesPerSplit) {
              splits.add(createFileSplit(fileName, begin, length));
              begin += length;
              length = 0;
              numLines = 0;
            }
          }
<!-- my part of concern end -->
          if (numLines != 0) {
            splits.add(createFileSplit(fileName, begin, length));
          }
        } finally {
          if (lr != null) {
            lr.close();
          }
        }
        return splits; 
      }

编辑以将我的用例提供给clément-mathieu

我的数据集是每个2gb的大输入文件。文件中的每一行代表一条需要插入数据库表的记录(在我的例子中是cassandra) 我想将数据库的批量事务限制为每个n行。 我已经使用nlineinputformat成功完成了这项工作。我唯一担心的是,是否存在可能出现在生产中的隐藏性能瓶颈。

1 个答案:

答案 0 :(得分:1)

  

基本上它意味着文件数据被寻找两次,一次是在分割创建过程中。从mapper任务中读取一次。

InputFormat的目的是为每个N行创建一个分割。计算拆分边界的唯一方法是读取此文件并查找换行符。这项操作成本很高,但如果这是您所需要的,则无法避免。

  

如果这是一个瓶颈,hadoop的工作如何覆盖这个?

不确定理解这个问题。

NLineInputFormat不是默认的InputFormat,很少有用例需要它。如果您阅读该类的javadoc,您将看到此类主要用于将参数提供给令人尴尬的并行作业(=“小”输入文件)。

大多数InputFormat不需要读取文件来计算拆分。它们通常使用硬性规则,例如拆分应为128MB 每个HDFS块的一个拆分,并且RecordReaders将处理实际的开始/拆分结束偏移。

如果NLineInputFormat.getSplitsForFile的费用是个问题,我会真的回顾为什么我需要使用这个InputFormat。您要做的是限制映射器中业务流程的批量大小。使用NLineInputFormat为每N行创建一个映射器,这意味着映射器永远不会执行多个批量事务。您似乎不需要此功能,您只想限制批量事务的大小,但不关心映射器是否按顺序执行其中几个。所以你要支付你发现的代码的成本作为回报。

我会使用TextInputFormat并在映射器中创建批处理。在伪代码中:

setup() {
  buffer = new Buffer<String>(1_000_000);
}

map(LongWritable key, Text value) {
  buffer.append(value.toString())
  if (buffer.isFull()) {
    new Transaction(buffer).doIt()
    buffer.clear()
  }
}

cleanup() {
  new Transaction(buffer).doIt()
  buffer.clear()
}

默认情况下,每个HDFS块都会创建一个映射器。如果您认为这太多或太少,mapred.(max|min).split.size变量允许增加或减少并行度。

基本上,虽然方便的NLineInputFormat对于你需要的东西来说太精细了。你可以使用TextInputFormat*.split.size一起玩,但不需要读取文件来创建分割。