1

假设一个客户端应用程序使用一个FileSplit对象来从相应的文件中读取实际字节。

为此,必须通过以下代码InputStream从 中创建一个对象:FileSplit

    FileSplit split = ... // The FileSplit reference
    FileSystem fs   = ... // The HDFS reference

    FSDataInputStream fsin = fs.open(split.getPath());

    long start = split.getStart()-1; // Byte before the first

    if (start >= 0)
    {
        fsin.seek(start);
    }

在某些场景中,例如类,会出现将流调整为 -1 的情况Hadoop MapReduce LineRecordReader。但是,该FSDataInputStream seek()方法的文档明确指出,在寻找到一个位置之后,下一次读取将来自该位置,这意味着(?)上面的代码将是 1 个字节(?)。

所以,问题是,对于所有 InputSplit 阅读案例是否需要“-1”调整?

顺便说一句,如果想要FileSplit正确读取一个文件,仅寻找它的开头是不够的,因为每个拆分也有一个结尾,可能与实际 HDFS 文件的结尾不同。因此,对应的InputStream应该是“有界的”,即具有最大长度,如下所示:

    InputStream is = new BoundedInputStream(fsin, split.getLength());

fsin在这种情况下,在上面创建了“本机”流之后,使用org.apache.commons.io.input.BoundedInputStream该类来实现“边界”。

更新

显然,仅当用例排在该类中时才需要进行调整LineRecordReader,这超出了拆分的边界,以确保它读取完整的最后一行。

可以在较早的问题MAPREDUCE-772的评论中找到有关此问题的详细讨论。

4

1 回答 1

2

寻找位置 0 意味着对 InputStream.read() 的下一次调用将读取字节 0。寻找位置 -1 很可能会引发异常。

当您在示例和源代码中谈论标准模式时,您具体指的是哪里?

正如您所注意到的,拆分不一定有界 - 以 TextInputFormat 和可以拆分的文件为例。处理拆分的记录读取器将:

  • 寻找起始索引,然后找到下一个换行符
  • 查找下一个换行符(或 EOF)并将该“行”作为下一条记录返回

重复此过程,直到找到的下一个换行符超过拆分的结尾,或者找到 EOF。因此,您会看到,在这种情况下,拆分的实际边界可能与输入拆分给出的边界右移

更新

从 LineRecordReader 引用此代码块:

if (codec != null) {
  in = new LineReader(codec.createInputStream(fileIn), job);
  end = Long.MAX_VALUE;
} else {
  if (start != 0) {
    skipFirstLine = true;
    --start;
    fileIn.seek(start);
  }
  in = new LineReader(fileIn, job);
}
if (skipFirstLine) {  // skip first line and re-establish "start".
  start += in.readLine(new Text(), 0,
                       (int)Math.min((long)Integer.MAX_VALUE, end - start));
}

--start语句很可能是为了避免从换行符开始拆分并返回一个空行作为第一条记录。可以看到,如果出现 seek ,第一行会被跳过,以确保文件拆分不会返回重叠记录

于 2013-04-23T23:31:53.397 回答