3

我尝试了不同的方法来创建一个大型 Hadoop 序列文件,只需一个短(<100 字节)键但一个大(> 1GB)值(BytesWriteable)。

以下示例适用于开箱即用:

https://svn.apache.org/repos/asf/hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/ hadoop/mapred/BigMapOutput.java

它写入多个随机长度的键和值,总大小> 3GB。

但是,这不是我想要做的。所以我使用 hadoop 2.2.0 API 将其修改为:

      Path file = new Path("/input");
      SequenceFile.Writer writer = SequenceFile.createWriter(conf,
      SequenceFile.Writer.file(file),
      SequenceFile.Writer.compression(CompressionType.NONE),
      SequenceFile.Writer.keyClass(BytesWritable.class),
      SequenceFile.Writer.valueClass(BytesWritable.class));
      int numBytesToWrite = fileSizeInMB * 1024 * 1024;
      BytesWritable randomKey = new BytesWritable();
      BytesWritable randomValue = new BytesWritable();
      randomKey.setSize(1);
      randomValue.setSize(numBytesToWrite);
      randomizeBytes(randomValue.getBytes(), 0, randomValue.getLength());
      writer.append(randomKey, randomValue);
      writer.close();

当 fileSizeInMB>700MB 时,我收到如下错误:

java.lang.NegativeArraySizeException
        at  org.apache.hadoop.io.BytesWritable.setCapacity(BytesWritable.java:144)
        at  org.apache.hadoop.io.BytesWritable.setSize(BytesWritable.java:123)
        ...

我看到正在讨论这个错误,但没有看到任何解决方案。请注意,int(2^32) 可以大到 2GB,它不应该在 700MB 时失败。

如果您有其他替代方法来创建如此大值的 SequenceFile,请告知。我尝试了其他方法,例如 IOutils.read 从输入流到字节 [],我得到了堆大小或 OOME。

4

2 回答 2

1

只需使用 ArrayPrimitiveWritable 代替。

BytesWritable中设置新容量会导致 int 溢出:

public void setSize(int size) {
    if (size > getCapacity()) {
       setCapacity(size * 3 / 2);
    }
    this.size = size;
}

700 Mb * 3 > 2Gb = int 溢出!

因此,您无法将超过 700 Mb 的数据反序列化(但可以写入和序列化)到 BytesWritable。

于 2015-08-28T11:14:51.247 回答
0

如果您想使用BytesWritable,之前有一个选项将容量设置得足够高,因此您使用 2GB,而不仅仅是 700MB:

randomValue.setCapacity(numBytesToWrite);
randomValue.setSize(numBytesToWrite); // will not resize now

这个错误最近已在 Hadoop 中修复,因此在较新的版本中即使没有它也应该可以工作:

public void setSize(int size) {
  if (size > getCapacity()) {
    // Avoid overflowing the int too early by casting to a long.
    long newSize = Math.min(Integer.MAX_VALUE, (3L * size) / 2L);
    setCapacity((int) newSize);
  }
  this.size = size;
}
于 2016-06-04T14:29:12.047 回答