2

我使用 protobuf 作为序列化程序来格式化磁盘上的数据。我可能有大量的 protobuf 对象,比如数百万个。在磁盘上布局它们的最佳选择是什么?protobuf 对象将按顺序逐一读取,或通过外部索引随机访问读取。

我曾经使用 lenghth(int)+protobuf_object+length(int).... 格式,但如果其中一个 protobuf 碰巧是脏的,它就会失败。如果许多 protobuf 对象很小,它可能会有一些开销。

4

2 回答 2

3

如果您只需要顺序访问,存储多条消息的最简单方法是在它之前写入对象的大小,如文档所建议的:http: //developers.google.com/protocol-buffers/docs/techniques#streaming

例如,您可以使用以下成员函数创建一个类“MessagesFile”来打开、读取和写入您的消息:

// File is opened using append mode and wrapped into
// a FileOutputStream and a CodedOutputStream
bool Open(const std::string& filename,
          int buffer_size = kDefaultBufferSize) {

    file_ = open(filename.c_str(),
                 O_WRONLY | O_APPEND | O_CREAT, // open mode
                 S_IREAD | S_IWRITE | S_IRGRP | S_IROTH | S_ISUID); //file permissions

    if (file_ != -1) {
        file_ostream_ = new FileOutputStream(file_, buffer_size);
        ostream_ = new CodedOutputStream(file_ostream_);
        return true;
    } else {
        return false;
    }
}

// Code for append a new message
bool Serialize(const google::protobuf::Message& message) {
    ostream_->WriteLittleEndian32(message.ByteSize());
    return message.SerializeToCodedStream(ostream_);
}

// Code for reading a message using a FileInputStream
// wrapped into a CodedInputStream 
bool Next(google::protobuf::Message *msg) {
    google::protobuf::uint32 size;
    bool has_next = istream_->ReadLittleEndian32(&size);
    if(!has_next) {
        return false;
    } else {
        CodedInputStream::Limit msgLimit = istream_->PushLimit(size);
        if ( msg->ParseFromCodedStream(istream_) ) {
            istream_->PopLimit(msgLimit);
            return true;
        }
        return false;
    }
}

然后,要编写您的消息,请使用:

MessagesFile file;
reader.Open("your_file.dat");

file.Serialize(your_message1);
file.Serialize(your_message2);
...
// close the file

要阅读您的所有消息:

MessagesFile reader;
reader.Open("your_file.dat");

MyMsg msg;
while( reader.Next(&msg) ) {
    // user your message
}
...
// close the file
于 2013-01-10T21:44:24.497 回答
2

(我希望我正确理解了您的问题,并且我的回答适合您的用例!)

将任意协议缓冲区消息流存储到磁盘的一种技术是定义一个包装器消息,其中所有字段都定义为repeated(这意味着optional),然后当您读取字节时,您将获得包装器类的实例并调用hasX() 方法来找到你实际拥有的东西。在您的情况下,这种方法的问题是您没有随机访问,也没有真正的流式传输(所有类型的消息都Foo将放在一起,然后是所有Bars),如果您的数据太大,您将无法容纳整个很多记忆。

实际上,您基本上是在寻求一种方法来存储任何类型的数据,以便可以流式传输或随机访问。这是一个通用问题,而不是特定于协议缓冲区的问题。

你的问题是:

  • 分隔记录...(见注)
  • ......以这样一种方式,可以检测到损坏并容忍或修复......
  • ...同时维护一个索引以允许随机访问

您可能会使用索引来允许进行某种完整性检查,但即使这样也需要一种机制来确保索引和数据对应并保持同步。

因此,它可能不是理想的解决方案,但实现您想要的一种方法,特别是如果完整性是一个问题,是将此信息存储在允许存储二进制数据并可以快速返回该数据的数据库中。随机访问和数据完整性的问题将成为数据库提供者的责任。任何能够存储 BLOB 的传统数据库都可以做到这一点,尽管我也会考虑将它存储在 NoSQL 中,例如 MongoDB。

笔记

如果您仔细定义您的协议缓冲区(即您知道所存储字段的类型和长度),那么您实际上不需要分隔您的记录,因为它们的长度永远不会改变。但是,这会破坏 Protocol Buffers 的特性之一,即其面向未来的特性。如果您以.proto固定消息大小的方式设计 a,您将无法添加新字段并仍然适合相同的文件格式,安全地说每条新消息在 x 字节后开始。

于 2013-01-09T13:05:56.740 回答