使用可变长度/非分隔二进制文件在hadoop中拆分

时间:2013-11-17 17:10:10

标签: hadoop gis openstreetmap hadoop-partitioning

我刚刚开始研究基于hadoop的ingester,用于打开街道地图数据。有几种格式 - 但我一直瞄准基于协议缓冲的格式(注意 - 它不是纯粹的pb)。

我认为将文件预分割成序列文件更有效 - 而不是在自定义记录阅读器/输入格式中处理可变长度编码 - 但是想要进行健全性检查。 / p>

格式在PBF Format Description更详细地描述 但基本上它是[BlobHeader,Blob]块的集合。

有一个Blob Header

message BlobHeader {
   required string type = 1;
   optional bytes indexdata = 2;
   required int32 datasize = 3;
 }

然后Blob(其大小由标题中的datasize参数定义)

 message Blob {
   optional bytes raw = 1; // No compression
   optional int32 raw_size = 2; // Only set when compressed, to the uncompressed size
   optional bytes zlib_data = 3;
   // optional bytes lzma_data = 4; // PROPOSED.
   // optional bytes OBSOLETE_bzip2_data = 5; // Deprecated.
 }

一旦你明显进入blob就会有更多的结构 - 但是我会在mapper中处理它 - 我想要做的是每个mapper最初有一个blob(后来可能是每个mapper的blob的一些)。 / p>

其他一些输入格式/记录阅读器使用“足够大”的分割大小,然后向后/向前搜索分隔符 - 但由于没有分隔符可以让我知道blob / header的偏移量 - 以及没有指向它们的索引 - 如果没有先通过文件流式传输,我看不到任何方法来获取我的分割点。

现在我不需要实际读取磁盘上的整个文件 - 我可以从读取标题开始,使用该信息来搜索blob,将其设置为第一个分割点,然后重复。但这是预分裂成序列文件的唯一选择,我可以想出来。

有没有更好的方法来处理这个 - 或者如果没有,对这两个建议的想法?

1 个答案:

答案 0 :(得分:4)

好吧,我去了解getSplits方法中的二进制文件 - 因为我正在跳过超过99%的数据,所以它很快(对于planet-osm 22GB世界文件约为20秒)。如果其他人偶然发现,这是getSplits方法。

@Override
public List<InputSplit> getSplits(JobContext context){
    List<InputSplit> splits = new ArrayList<InputSplit>();
    FileSystem fs = null;
    Path file = OSMPBFInputFormat.getInputPaths(context)[0]; 
    FSDataInputStream in = null;
    try {
        fs = FileSystem.get(context.getConfiguration());
        in = fs.open(file);
        long pos = 0;
        while (in.available() > 0){
            int len = in.readInt(); 
            byte[] blobHeader = new byte[len]; 
            in.read(blobHeader);
            BlobHeader h = BlobHeader.parseFrom(blobHeader);
            FileSplit split = new FileSplit(file, pos,len + h.getDatasize(), new String[] {});
            splits.add(split);
            pos += 4;
            pos += len;
            pos += h.getDatasize();
            in.skip(h.getDatasize());
        }
    } catch (IOException e) {
        sLogger.error(e.getLocalizedMessage());
    } finally {
        if (in != null) {try {in.close();}catch(Exception e){}};
        if (fs != null) {try {fs.close();}catch(Exception e){}};
    }
    return splits;
}
到目前为止,工作正常 - 尽管我还没有将输出归功于输出。它比将pbf复制到hdfs,转换为单个映射器中的序列,然后摄取(复制时间占主导地位)更快。它也比将外部程序复制到hdfs中的序列文件快20%,然后针对hdfs运行映射器(脚本为后者)。 所以这里没有抱怨。

请注意,这会为每个块生成一个映射器 - 这是行星世界文件的~23k映射器。我实际上是每次拆分捆绑多个块 - 只需循环x次,然后将拆分添加到集合中。

对于BlobHeader,我刚从上面的OSM wiki链接编译了protobuf .proto文件。如果需要,您也可以从OSM二进制类中预先生成它 - maven fragment是:

<dependency>
    <groupId>org.openstreetmap.osmosis</groupId>
    <artifactId>osmosis-osm-binary</artifactId>
    <version>0.43-RELEASE</version>
</dependency>