2

ディスク上のデータをフォーマットするためのシリアライザーとして protobuf を使用しています。私はprotobufオブジェクトの大規模なセット、たとえば何百万ものものを持っているかもしれません。それらをディスク上にレイアウトするための最良の選択は何ですか? protobuf オブジェクトは、1 つずつ順番に読み取られるか、外部インデックスによってランダムにアクセスされて読み取られます。

以前は length(int)+protobuf_object+length(int).... 形式を使用していましたが、protobuf の 1 つがダーティであると失敗しました。また、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

(あなたの質問を正しく理解し、私の回答があなたのユースケースに合っていることを願っています!)

プロトコル バッファ メッセージの任意のストリームをディスクに格納するための 1 つの手法は、すべてのフィールドが として定義されているラッパー メッセージを定義することですrepeated(これは を意味optionalします)。次に、バイトを読み取るときに、ラッパー クラスのインスタンスを取得し、 hasX() メソッドを使用して、実際に持っているものを見つけます。あなたの場合のこのアプローチの問題は、ランダムアクセスも実際のストリーミングも得られないことです (タイプのすべてのメッセージFooが一緒になり、その後にすべてBarの s が続きます)。データが大きすぎると、全体に収まりません。記憶にたくさん。

実際、あなたは基本的に、あらゆる種類のデータをストリーミングまたはランダム アクセスできるように保存するための方法論を求めています。これは、プロトコル バッファに固有の問題ではなく、一般的な問題です。

あなたの問題は次のとおりです。

  • 区切りレコード... (注を参照)
  • ...損傷を検出し、許容または修復できるような方法で...
  • ...ランダムアクセスを許可するインデックスを維持しながら

おそらくインデックスを使用して何らかの整合性チェックを許可するでしょうが、それでもインデックスとデータが対応し、同期を維持するためのメカニズムが必要です。

したがって、これは理想的な解決策ではないかもしれませんが、特に整合性が問題となる場合に、目的を達成する 1 つの方法は、バイナリ データの格納を許可し、そのデータをすばやく返すことができるデータベースにこの情報を格納することです。ランダム アクセスとデータの整合性の問題は、データベース プロバイダーの責任になります。BLOB を格納できる従来のデータベースであれば、それを実行できますが、MongoDB などの NoSQL に格納することも検討します。

ノート

Protocol Buffers を慎重に定義する場合 (つまり、格納されるフィールドのタイプと長さを知っている場合) は、レコードの長さが変わらないため、実際にレコードを区切る必要はありません。ただし、これは Protocol Buffers の機能の 1 つ、つまり将来性のある性質を損なうことになります。メッセージ サイズが固定されるようにを設計した場合.proto、新しいフィールドを追加することはできず、同じファイル形式に収まることができず、新しいメッセージはそれぞれ x バイトの後に始まると安全に言えます。

于 2013-01-09T13:05:56.740 に答える