เก็บชุดของ protobuf ไว้บนดิสก์

ฉันใช้ protobuf เป็นซีเรียลไลเซอร์เพื่อฟอร์แมตข้อมูลบนดิสก์ ฉันอาจมีวัตถุโปรโตบัฟชุดใหญ่ เช่น ล้านชิ้น ตัวเลือกที่ดีที่สุดในการจัดวางลงบนดิสก์คืออะไร? วัตถุ protobuf จะถูกอ่านตามลำดับทีละรายการหรือเข้าถึงโดยสุ่มโดยดัชนีภายนอก

ฉันเคยใช้รูปแบบ lenghth(int)+protobuf_object+length(int).... แต่มันล้มเหลวหากหนึ่งใน protobuf สกปรก และถ้าวัตถุ protobuf จำนวนมากมีขนาดเล็ก ก็อาจมีโอเวอร์อยู่บ้าง


person Shawn    schedule 09.01.2013    source แหล่งที่มา
comment
การทราบว่าคุณต้องการเข้าถึงข้อมูลอย่างไร อาจมีประโยชน์: แค่อ่านตามลำดับ เข้าถึงแบบสุ่ม เขียนแบบสุ่ม ค้นหาตามเกณฑ์บางอย่าง   -  person jpa    schedule 09.01.2013
comment
กำหนดว่าล้มเหลวเมื่อโปรโตบัฟตัวใดตัวหนึ่งสกปรก คุณหมายความว่าฉันไม่สามารถเขียนทับส่วนนั้นของไฟล์ได้ เพราะหากความยาวเปลี่ยนไป อาจมีช่องว่างในไฟล์ (มีขยะ) หรือเขียนทับข้อมูลบิตถัดไป   -  person Marc Gravell    schedule 09.01.2013
comment
ใช่ ฉันแค่อยากจะอ่านมันตามลำดับ หรือค้นหาตาม 'ดัชนี' ที่ใส่ไว้ในไฟล์ ไม่จำเป็นต้องเขียนแบบสุ่ม   -  person Shawn    schedule 09.01.2013


คำตอบ (2)


หากคุณต้องการเพียงการเข้าถึงตามลำดับ วิธีที่ง่ายที่สุดในการจัดเก็บหลายข้อความคือการเขียนขนาดของวัตถุที่อยู่ข้างหน้า ตามที่แนะนำในเอกสารประกอบ: 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
person aeciosan    schedule 10.01.2013

(ฉันหวังว่าฉันจะเข้าใจคำถามของคุณถูกต้องและคำตอบของฉันเหมาะกับกรณีการใช้งานของคุณ!)

เทคนิคหนึ่งสำหรับการจัดเก็บสตรีมข้อความ Protocol Buffer ลงดิสก์ตามอำเภอใจคือการกำหนดข้อความ wrapper โดยที่ฟิลด์ทั้งหมดถูกกำหนดเป็น repeated (ซึ่งหมายถึง optional) จากนั้นเมื่อคุณอ่านในหน่วยไบต์ คุณจะได้รับอินสแตนซ์ของคลาส wrapper และ เรียกใช้เมธอด hasX() เพื่อค้นหาสิ่งที่คุณมีจริง ปัญหาของแนวทางนี้ในกรณีของคุณคือ คุณไม่สามารถเข้าถึงแบบสุ่มและไม่มีการสตรีมจริง (ข้อความประเภท Foo ทั้งหมดจะรวมกัน ตามด้วย Bars ทั้งหมด) และหากข้อมูลของคุณใหญ่เกินไป คุณจะไม่สามารถใส่ข้อมูลได้พอดี มากมายในความทรงจำ

ที่จริงแล้วคุณกำลังขอวิธีการจัดเก็บข้อมูลประเภทใดก็ตามในลักษณะที่สามารถสตรีมหรือเข้าถึงแบบสุ่มได้ นี่เป็นปัญหาทั่วไป แทนที่จะเป็นปัญหาเฉพาะเจาะจงกับบัฟเฟอร์โปรโตคอล

ปัญหาของคุณคือ:

  • บันทึกการกำหนดเขต... (ดูหมายเหตุ)
  • ...ในลักษณะที่สามารถตรวจพบความเสียหายและอดทนหรือซ่อมแซมได้...
  • ...ในขณะที่ยังคงรักษาดัชนีไว้เพื่อให้เข้าถึงแบบสุ่มได้

คุณอาจใช้ดัชนีเพื่ออนุญาตการตรวจสอบความสมบูรณ์บางประเภท แต่ถึงแม้จะต้องใช้กลไกเพื่อให้แน่ใจว่าดัชนีและข้อมูลสอดคล้องกันและยังคงซิงค์อยู่

ดังนั้น จึงอาจไม่ใช่วิธีแก้ปัญหาในอุดมคติ แต่วิธีหนึ่งในการบรรลุสิ่งที่คุณต้องการ โดยเฉพาะอย่างยิ่งหากความสมบูรณ์เป็นปัญหา คือการจัดเก็บข้อมูลนี้ไว้ในฐานข้อมูลที่อนุญาตให้มีการจัดเก็บข้อมูลไบนารีและสามารถส่งคืนข้อมูลนั้นได้อย่างรวดเร็ว คำถามเกี่ยวกับการเข้าถึงแบบสุ่มและความสมบูรณ์ของข้อมูลจะกลายเป็นความรับผิดชอบของผู้ให้บริการฐานข้อมูล ฐานข้อมูลแบบเดิมใดๆ ที่สามารถจัดเก็บ BLOB ได้จะสามารถทำได้ แม้ว่าฉันจะพิจารณาเก็บไว้ใน NoSQL เช่น MongoDB ด้วย

บันทึก

หากคุณกำหนดบัฟเฟอร์โปรโตคอลของคุณอย่างระมัดระวัง (เช่น คุณทราบประเภทและความยาวของฟิลด์ที่กำลังจัดเก็บ) คุณไม่จำเป็นต้องกำหนดขอบเขตบันทึกของคุณจริงๆ เนื่องจากความยาวจะไม่มีวันเปลี่ยนแปลง อย่างไรก็ตาม การดำเนินการนี้จะทำลายคุณลักษณะอย่างหนึ่งของ Protocol Buffers กล่าวคือ ลักษณะที่พิสูจน์ได้ในอนาคต หากคุณออกแบบ .proto ในลักษณะที่ขนาดข้อความได้รับการแก้ไข คุณจะไม่สามารถเพิ่มฟิลด์ใหม่และยังคงจัดอยู่ในรูปแบบไฟล์เดียวกัน โดยบอกอย่างปลอดภัยว่าแต่ละข้อความใหม่เริ่มต้นหลังจาก x ไบต์

person Rich    schedule 09.01.2013