Performant Entity Serialization: BSON vs MessagePack (เทียบกับ JSON)


137

เมื่อเร็ว ๆ นี้ฉันพบMessagePackซึ่งเป็นรูปแบบอนุกรมไบนารีทางเลือกสำหรับProtocol BuffersและJSONของ Googleซึ่งมีประสิทธิภาพดีกว่าทั้งสองอย่าง

นอกจากนี้ยังมีBSONรูปแบบอนุกรมที่ MongoDB ใช้ในการจัดเก็บข้อมูล

ใครสามารถอธิบายความแตกต่างและข้อดี / ข้อได้เปรียบของ BSON vs MessagePack ได้หรือไม่?


เพียงแค่ให้เสร็จสมบูรณ์รายการ performant รูปแบบอนุกรมไบนารี: นอกจากนี้ยังมีGobs ซึ่งกำลังจะเป็นตัวตายตัวแทนของของ Google พิธีสารบัฟเฟอร์ อย่างไรก็ตามในทางตรงกันข้ามกับรูปแบบอื่น ๆ ที่กล่าวถึงทั้งหมดที่ไม่เข้าใจภาษาและอาศัยการสะท้อนในตัวของ Goนอกจากนี้ยังมีไลบรารี Gobs สำหรับภาษาอื่นที่ไม่ใช่ Go


3
ดูเหมือนส่วนใหญ่จะเป็นโฆษณาทางการตลาด ประสิทธิภาพของรูปแบบอนุกรม ["คอมไพล์"] เกิดจากการนำไปใช้งาน แม้ว่ารูปแบบบางรูปแบบจะมีค่าใช้จ่ายมากกว่าปกติ (เช่น JSON เนื่องจากมีการประมวลผลแบบไดนามิกทั้งหมด) แต่รูปแบบเองก็ไม่มี "ความเร็ว" จากนั้นหน้าจะไป "เลือกและเลือก" ว่าจะเปรียบเทียบตัวเองอย่างไร ... มันเป็นแฟชั่นที่ไม่เป็นกลางมาก ไม่ใช่ถ้วยชาของฉัน

6
การแก้ไข: Gobs ไม่ได้มีไว้เพื่อแทนที่ Protocol Buffers และอาจจะไม่เกิดขึ้นอีก นอกจากนี้ Gobs ยังเป็นภาษาที่ไม่เชื่อเรื่องพระเจ้า (อ่าน / เขียนได้ในภาษาใดก็ได้โปรดดูcode.google.com/p/libgob ) แต่มีการกำหนดให้ตรงกับวิธีที่ Go จัดการกับข้อมูลอย่างใกล้ชิดดังนั้นจึงทำงานได้ดีที่สุดกับ Go
Kyle C

6
ลิงก์ไปยังเกณฑ์มาตรฐานประสิทธิภาพของ msgpack เสีย ( msgpack.org/index/speedtest.png )
Aliaksei Ramanau

คำตอบ:


199

// โปรดทราบว่าฉันเป็นผู้เขียน MessagePack คำตอบนี้อาจมีความลำเอียง

การออกแบบรูปแบบ

  1. เข้ากันได้กับ JSON

    แม้จะมีชื่อ แต่ความเข้ากันได้ของ BSON กับ JSON นั้นไม่ดีนักเมื่อเทียบกับ MessagePack

    BSON มีประเภทพิเศษเช่น "ObjectId", "Min key", "UUID" หรือ "MD5" (ฉันคิดว่า MongoDB ต้องการประเภทเหล่านี้) ประเภทเหล่านี้เข้ากันไม่ได้กับ JSON นั่นหมายความว่าข้อมูลบางประเภทอาจสูญหายได้เมื่อคุณแปลงวัตถุจาก BSON เป็น JSON แต่แน่นอนก็ต่อเมื่อประเภทพิเศษเหล่านี้อยู่ในแหล่ง BSON เท่านั้น อาจเป็นข้อเสียที่จะใช้ทั้ง JSON และ BSON ในบริการเดียว

    MessagePack ได้รับการออกแบบให้แปลงจาก / เป็น JSON ได้อย่างโปร่งใส

  2. MessagePack มีขนาดเล็กกว่า BSON

    รูปแบบของ MessagePack น้อยกว่า BSON เป็นผลให้ MessagePack สามารถซีเรียลไลซ์วัตถุที่มีขนาดเล็กกว่า BSON

    ตัวอย่างเช่นแผนที่แบบง่าย {"a": 1, "b": 2} ถูกทำให้เป็นอนุกรม 7 ไบต์ด้วย MessagePack ในขณะที่ BSON ใช้ 19 ไบต์

  3. BSON รองรับการอัปเดตในสถานที่

    ด้วย BSON คุณสามารถแก้ไขบางส่วนของวัตถุที่จัดเก็บโดยไม่ต้องทำให้เป็นอนุกรมของวัตถุทั้งหมดอีกครั้ง สมมติว่าแผนที่ {"a": 1, "b": 2} ถูกเก็บไว้ในไฟล์และคุณต้องการอัปเดตค่าของ "a" ตั้งแต่ 1 ถึง 2000

    เมื่อใช้ MessagePack 1 จะใช้เพียง 1 ไบต์ แต่ 2000 ใช้ 3 ไบต์ ดังนั้นต้องย้าย "b" ไปข้างหลัง 2 ไบต์ในขณะที่ "b" จะไม่ถูกแก้ไข

    ด้วย BSON ทั้ง 1 และ 2000 ใช้ 5 ไบต์ เนื่องจากความฟุ่มเฟื่อยนี้คุณไม่จำเป็นต้องย้าย "b"

  4. MessagePack มี RPC

    MessagePack, Protocol Buffers, Thrift และ Avro รองรับ RPC แต่ BSON ไม่ทำ

ความแตกต่างเหล่านี้บ่งบอกว่า MessagePack ได้รับการออกแบบมาสำหรับการสื่อสารบนเครือข่ายในขณะที่ BSON ออกแบบมาสำหรับการจัดเก็บ

การใช้งานและการออกแบบ API

  1. MessagePack มี API การตรวจสอบประเภท (Java, C ++ และ D)

    MessagePack รองรับการพิมพ์แบบคงที่

    การพิมพ์แบบไดนามิกที่ใช้กับ JSON หรือ BSON มีประโยชน์สำหรับภาษาแบบไดนามิกเช่น Ruby, Python หรือ JavaScript แต่เป็นปัญหาสำหรับภาษาคงที่ คุณต้องเขียนรหัสการตรวจสอบประเภทที่น่าเบื่อ

    MessagePack มี API การตรวจสอบประเภท มันแปลงวัตถุที่พิมพ์แบบไดนามิกเป็นวัตถุที่พิมพ์แบบคงที่ นี่คือตัวอย่างง่ายๆ (C ++):

    #include <msgpack.hpp>

    class myclass {
    private:
        std::string str;
        std::vector<int> vec;
    public:
        // This macro enables this class to be serialized/deserialized
        MSGPACK_DEFINE(str, vec);
    };

    int main(void) {
        // serialize
        myclass m1 = ...;

        msgpack::sbuffer buffer;
        msgpack::pack(&buffer, m1);

        // deserialize
        msgpack::unpacked result;
        msgpack::unpack(&result, buffer.data(), buffer.size());

        // you get dynamically-typed object
        msgpack::object obj = result.get();

        // convert it to statically-typed object
        myclass m2 = obj.as<myclass>();
    }
  1. MessagePack มี IDL

    มันเกี่ยวข้องกับ API การตรวจสอบประเภท MessagePack รองรับ IDL (ข้อมูลจำเพาะได้จาก: http://wiki.msgpack.org/display/MSGPACK/Design+of+IDL )

    Protocol Buffers และ Thrift ต้องการ IDL (ไม่รองรับการพิมพ์แบบไดนามิก) และให้การใช้งาน IDL ที่เป็นผู้ใหญ่มากขึ้น

  2. MessagePack มี API การสตรีม (Ruby, Python, Java, C ++, ... )

    MessagePack รองรับการสตรีม deserializers คุณสมบัตินี้มีประโยชน์สำหรับการสื่อสารบนเครือข่าย นี่คือตัวอย่าง (Ruby):

    require 'msgpack'

    # write objects to stdout
    $stdout.write [1,2,3].to_msgpack
    $stdout.write [1,2,3].to_msgpack

    # read objects from stdin using streaming deserializer
    unpacker = MessagePack::Unpacker.new($stdin)
    # use iterator
    unpacker.each {|obj|
      p obj
    }

33
MessagePack เปรียบเทียบกับ Google Protobufs อย่างไรในแง่ของขนาดข้อมูลและส่งผลต่อประสิทธิภาพการทำงานของอากาศ
Ellis

4
จุดแรกกล่าวถึงข้อเท็จจริงที่ว่า MessagePack มีความสามารถในการเป็นไบต์ดิบซึ่งไม่สามารถแสดงใน JSON ได้ ดังนั้นมันก็เหมือนกับ BSON ในเรื่องนั้น ...

4
@lttlrck โดยทั่วไปแล้วไบต์ดิบจะถือว่าเป็นสตริง (โดยปกติจะเป็น utf-8) เว้นแต่คาดว่าจะเป็นอย่างอื่นและตกลงกันทั้งสองด้านของแชแนล msgpack ใช้เป็นรูปแบบสตรีม / ซีเรียลไลเซชัน ... และมีรายละเอียดน้อยกว่าที่ json .. แม้ว่าจะมีคนอ่านน้อยกว่าก็ตาม
Tracker1

4
"MessagePack มี API การตรวจสอบประเภท BSON ไม่ได้" ไม่ถูกต้องทั้งหมด นี่เป็นเรื่องจริงสำหรับการใช้งาน BSON ในภาษาที่พิมพ์แบบคงที่เช่นกัน
Brandon Black

1
ขณะนี้ MessagePack มีชนิดข้อมูล BINARY ดังนั้นอาร์กิวเมนต์ของ 1-1 de-serialization เข้ากันได้กับ JSON จึงไม่เป็นความจริงอีกต่อไป
zimbatm

16

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

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

สภาพแวดล้อมบางอย่างอาจมีการทำให้เป็นอนุกรมและการดีซีเรียลไลเซชั่นที่รวดเร็วมากไปยัง / จาก msgpack / protobuf's อื่น ๆ ไม่มากนัก โดยทั่วไปยิ่งภาษา / สภาพแวดล้อมอยู่ในระดับต่ำมากเท่าใดการทำให้อนุกรมไบนารีก็จะทำงานได้ดีขึ้น ในภาษาระดับที่สูงขึ้น (node.js, .Net, JVM) คุณมักจะเห็นว่าการทำให้อนุกรม JSON เร็วขึ้นจริง คำถามจะกลายเป็นว่าค่าใช้จ่ายเครือข่ายของคุณมีข้อ จำกัด มากกว่าหน่วยความจำ / cpu ของคุณหรือไม่?

เกี่ยวกับ msgpack vs bson vs protocol บัฟเฟอร์ ... msgpack เป็นไบต์ที่น้อยที่สุดของกลุ่มโปรโตคอลบัฟเฟอร์จะเหมือนกัน BSON กำหนดประเภทเนทีฟที่กว้างกว่าอีกสองประเภทและอาจเข้ากันได้ดีกว่ากับโหมดอ็อบเจ็กต์ของคุณ แต่จะทำให้มีรายละเอียดมากขึ้น บัฟเฟอร์โปรโตคอลมีข้อได้เปรียบในการออกแบบมาเพื่อสตรีม ... ซึ่งทำให้รูปแบบที่เป็นธรรมชาติมากขึ้นสำหรับรูปแบบการถ่ายโอน / การจัดเก็บไบนารี

โดยส่วนตัวแล้วฉันจะเอนเอียงไปที่ความโปร่งใสที่ JSON นำเสนอโดยตรงเว้นแต่จะมีความต้องการที่ชัดเจนสำหรับการรับส่งข้อมูลที่ลดลง บน HTTP ที่มีข้อมูล gzipped ความแตกต่างของค่าโสหุ้ยเครือข่ายยังเป็นปัญหาน้อยกว่าระหว่างรูปแบบ


6
พื้นเมือง MsgPack เป็นเพียงมีประสิทธิภาพด้วย ProtocolBuffers ขนาดฉลาดความยาวของคีย์ (ซึ่งมักจะนำเสนอข้อความ) เป็นระยะสั้นเช่น "เป็น" หรือ "ข" - หรือเป็นอย่างอื่นเป็นส่วนที่ไม่มีนัยสำคัญของน้ำหนักบรรทุกทั้งหมด พวกเขามักจะสั้นใน ProtocolBuffers ซึ่งใช้ IDL / คอมไพล์เพื่อแมปตัวบอกฟิลด์กับรหัส นี่คือสิ่งที่ทำให้ MsgPack เป็น "ไดนามิก" ซึ่ง ProtocolBuffers ไม่ใช่อย่างแน่นอนที่สุด ..
user2864740

2
จุดสิ้นสุดเป็นสิ่งที่ดี: gzip / deflate นั้นดีมากในการจัดการความซ้ำซ้อนของคีย์ในกรณีที่คีย์ดังกล่าว "ยาวกว่า แต่ซ้ำมาก" (MsgPack, JSON / BSON และ XML และอื่น ๆ ในหลาย ๆระเบียน) แต่จะไม่ช่วย ProtocolBuffers เลยที่นี่ .. Avro ทำการกำจัดคีย์ซ้ำซ้อนด้วยตนเองโดยการส่งสคีมาแยกกัน
user2864740

4

การทดสอบด่วนแสดงให้เห็นว่า JSON ที่ย่อขนาดได้ถูก deserialized เร็วกว่าไบนารี MessagePack ในการทดสอบ Article.json คือ 550kb minified JSON, Article.mpack คือ 420kb MP-version ของมัน อาจเป็นปัญหาการนำไปใช้งานแน่นอน

MessagePack:

//test_mp.js
var msg = require('msgpack');
var fs = require('fs');

var article = fs.readFileSync('Article.mpack');

for (var i = 0; i < 10000; i++) {
    msg.unpack(article);    
}

JSON:

// test_json.js
var msg = require('msgpack');
var fs = require('fs');

var article = fs.readFileSync('Article.json', 'utf-8');

for (var i = 0; i < 10000; i++) {
    JSON.parse(article);
}

เวลาคือ:

Anarki:Downloads oleksii$ time node test_mp.js 

real    2m45.042s
user    2m44.662s
sys     0m2.034s

Anarki:Downloads oleksii$ time node test_json.js 

real    2m15.497s
user    2m15.458s
sys     0m0.824s

จึงประหยัดพื้นที่ แต่เร็วกว่า? ไม่

เวอร์ชันทดสอบ:

Anarki:Downloads oleksii$ node --version
v0.8.12
Anarki:Downloads oleksii$ npm list msgpack
/Users/oleksii
└── msgpack@0.1.7  

7
แน่นอนขึ้นอยู่กับการใช้งาน การทดสอบของฉันกับ Python 2.7.3 การคลาย 489K test.json (409K เทียบเท่า test.msgpack) แสดงให้เห็นว่าการทำซ้ำ 10,000 ครั้งsimplejson2.6.2 ใช้เวลา 66.7 วินาทีและmsgpack0.2.2 ใช้เวลาเพียง 28.8
วันที่

2
Article.json นี้มาจากไหน?
Ant6n

คนรหัสทดสอบอยู่ในความคิดเห็นของฉันด้านบนคุณคาดหวังอะไรอีก Article.json เป็นวัตถุต่อเนื่อง json จากโครงการของเรา และถึงตอนนี้ผลลัพธ์เหล่านั้นอาจไม่เกี่ยวข้องกันเลยก็ได้
Oleksiy Khilkevich

14
นี่ไม่ใช่การเปรียบเทียบประสิทธิภาพที่ยุติธรรมเนื่องจาก JS มีการนำ JSON มาใช้ใน C ++ ในขณะที่ msgpack ใน JS
Alex Panchenko

2
คุณกำลังพยายามทำให้ MessagePack พูดภาษาละตินได้ดีกว่าชาวโรมัน JSON เป็นแบบดั้งเดิม (C ++) สำหรับ JavaScript ในขณะที่ MessagePack เขียนด้วย JavaScript ซึ่งถูกตีความ โดยพื้นฐานแล้วจะเป็นการเปรียบเทียบข้อมูลโค้ดสองชุดโดยชุดหนึ่งเขียนด้วย JavaScript และอีกชุดหนึ่งเขียนด้วย C ++
Ramazan Polat

0

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

document    ::=     int32 e_list

สิ่งนี้มีประโยชน์หลักสองประการสำหรับสภาพแวดล้อมที่ จำกัด (เช่นฝังตัว) ซึ่งขนาดและประสิทธิภาพเป็นสิ่งสำคัญ

  1. คุณสามารถตรวจสอบได้ทันทีว่าข้อมูลที่คุณจะแยกวิเคราะห์เป็นเอกสารที่สมบูรณ์หรือไม่หรือหากคุณจำเป็นต้องขอเพิ่มเติม ณ จุดใดจุดหนึ่ง (ไม่ว่าจะเป็นจากการเชื่อมต่อหรือที่เก็บข้อมูลบางส่วน) เนื่องจากนี่เป็นไปได้มากว่าเป็นการดำเนินการแบบอะซิงโครนัสคุณจึงอาจส่งคำขอใหม่ก่อนที่จะแยกวิเคราะห์
  2. ข้อมูลของคุณอาจมีเอกสารย่อยทั้งหมดที่มีข้อมูลที่ไม่เกี่ยวข้องสำหรับคุณ BSON ช่วยให้คุณสามารถข้ามไปยังวัตถุถัดไปผ่านเอกสารย่อยได้อย่างง่ายดายโดยใช้ข้อมูลขนาดของเอกสารย่อยเพื่อข้ามไป ในทางกลับกัน msgpack มีจำนวนองค์ประกอบภายในสิ่งที่เรียกว่าแผนที่ (คล้ายกับเอกสารย่อยของ BSON) แม้ว่านี่จะเป็นข้อมูลที่มีประโยชน์อย่างไม่ต้องสงสัย แต่ก็ไม่ได้ช่วยตัวแยกวิเคราะห์ คุณยังคงต้องแยกวิเคราะห์วัตถุทุกชิ้นในแผนที่และไม่สามารถข้ามไปได้ สิ่งนี้อาจมีผลกระทบอย่างมากต่อประสิทธิภาพทั้งนี้ขึ้นอยู่กับโครงสร้างของข้อมูลของคุณ

0

ฉันสร้างเกณฑ์มาตรฐานอย่างรวดเร็วเพื่อเปรียบเทียบความเร็วในการเข้ารหัสและถอดรหัสของ MessagePack กับ BSON BSON เร็วกว่าอย่างน้อยถ้าคุณมีไบนารีอาร์เรย์ขนาดใหญ่:

BSON writer: 2296 ms (243487 bytes)
BSON reader: 435 ms
MESSAGEPACK writer: 5472 ms (243510 bytes)
MESSAGEPACK reader: 1364 ms

ใช้ C # Newtonsoft.Json และ MessagePack โดย neuecc:

    public class TestData
    {
        public byte[] buffer;
        public bool foobar;
        public int x, y, w, h;
    }

    static void Main(string[] args)
    {
        try
        {
            int loop = 10000;

            var buffer = new TestData();
            TestData data2;
            byte[] data = null;
            int val = 0, val2 = 0, val3 = 0;

            buffer.buffer = new byte[243432];

            var sw = new Stopwatch();

            sw.Start();
            for (int i = 0; i < loop; i++)
            {
                data = SerializeBson(buffer);
                val2 = data.Length;
            }

            var rc1 = sw.ElapsedMilliseconds;

            sw.Restart();
            for (int i = 0; i < loop; i++)
            {
                data2 = DeserializeBson(data);
                val += data2.buffer[0];
            }
            var rc2 = sw.ElapsedMilliseconds;

            sw.Restart();
            for (int i = 0; i < loop; i++)
            {
                data = SerializeMP(buffer);
                val3 = data.Length;
                val += data[0];
            }

            var rc3 = sw.ElapsedMilliseconds;

            sw.Restart();
            for (int i = 0; i < loop; i++)
            {
                data2 = DeserializeMP(data);
                val += data2.buffer[0];
            }
            var rc4 = sw.ElapsedMilliseconds;

            Console.WriteLine("Results:", val);
            Console.WriteLine("BSON writer: {0} ms ({1} bytes)", rc1, val2);
            Console.WriteLine("BSON reader: {0} ms", rc2);
            Console.WriteLine("MESSAGEPACK writer: {0} ms ({1} bytes)", rc3, val3);
            Console.WriteLine("MESSAGEPACK reader: {0} ms", rc4);
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }

        Console.ReadLine();
    }

    static private byte[] SerializeBson(TestData data)
    {
        var ms = new MemoryStream();

        using (var writer = new Newtonsoft.Json.Bson.BsonWriter(ms))
        {
            var s = new Newtonsoft.Json.JsonSerializer();
            s.Serialize(writer, data);
            return ms.ToArray();
        }
    }

    static private TestData DeserializeBson(byte[] data)
    {
        var ms = new MemoryStream(data);

        using (var reader = new Newtonsoft.Json.Bson.BsonReader(ms))
        {
            var s = new Newtonsoft.Json.JsonSerializer();
            return s.Deserialize<TestData>(reader);
        }
    }

    static private byte[] SerializeMP(TestData data)
    {
        return MessagePackSerializer.Typeless.Serialize(data);
    }

    static private TestData DeserializeMP(byte[] data)
    {
        return (TestData)MessagePackSerializer.Typeless.Deserialize(data);
    }

0

อย่างที่ผู้เขียนกล่าวไว้, MessagePack ถูกออกแบบมาสำหรับการสื่อสารบนเครือข่ายในขณะที่ BSON ได้รับการออกแบบมาสำหรับการจัดเก็บ

MessagePack มีขนาดกะทัดรัดในขณะที่ BSON เป็นแบบ verbose MessagePack หมายถึงการประหยัดพื้นที่ในขณะที่ BSON ออกแบบมาสำหรับ CURD (ประหยัดเวลา)

ที่สำคัญที่สุดระบบประเภทของ MessagePack (คำนำหน้า) ตามการเข้ารหัส Huffman ที่นี่ฉันวาดโครงสร้าง Huffman ของ MessagePack (คลิกลิงค์เพื่อดูภาพ):

Huffman Tree of MessagePack

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.