Tuần tự thực thể thực hiện: BSON vs MessagePack (so với JSON)


137

Gần đây tôi đã tìm thấy MessagePack , một định dạng tuần tự hóa nhị phân thay thế cho Bộ đệm giao thứcJSON của Google, nó cũng vượt trội hơn cả hai.

Ngoài ra còn có định dạng tuần tự hóa BSON được MongoDB sử dụng để lưu trữ dữ liệu.

Ai đó có thể giải thích sự khác biệt và lợi thế của BSON so với MessagePack không?


Chỉ cần hoàn thành danh sách các định dạng tuần tự hóa nhị phân hiệu suất: Ngoài ra còn có các yêu tinh sẽ là người kế thừa Bộ đệm giao thức của Google . Tuy nhiên, trái ngược với tất cả các định dạng được đề cập khác, những ngôn ngữ không liên quan đến ngôn ngữ và dựa vào sự phản chiếu tích hợp của Go, cũng có các thư viện Gob cho ít nhất là ngôn ngữ khác ngoài Go.


3
Có vẻ như là một tải của quảng cáo tiếp thị. Hiệu suất của định dạng tuần tự hóa ["đã biên dịch"] là do việc triển khai được sử dụng. Mặc dù một số định dạng vốn đã có nhiều chi phí hơn (ví dụ JSON như tất cả được xử lý động), bản thân các định dạng không "có tốc độ". Sau đó, trang tiếp tục "chọn và chọn" cách so sánh chính nó ... nó là một kiểu rất không thiên vị. Không phải tách trà của tôi.

6
Sửa chữa: Yêu tinh không có ý định thay thế Bộ đệm giao thức, và có lẽ sẽ không bao giờ. Ngoài ra, các yêu tinh là ngôn ngữ bất khả tri (chúng có thể được đọc / viết bằng bất kỳ ngôn ngữ nào, xem code.google.com/p/libgob ), nhưng chúng được xác định để khớp chặt chẽ với cách Go xử lý dữ liệu, để chúng hoạt động tốt nhất với Go.
Kyle C

6
Liên kết đến các tiêu chuẩn hiệu suất của trình đóng gói đã bị hỏng ( Trình tin gói tin.org/index/speedtest.png ).
Aliaksei Ramanau

Câu trả lời:


197

// Xin lưu ý rằng tôi là tác giả của MessagePack. Câu trả lời này có thể bị sai lệch.

Thiết kế định dạng

  1. Khả năng tương thích với JSON

    Mặc dù tên của nó, khả năng tương thích của BSON với JSON không quá tốt so với MessagePack.

    BSON có các loại đặc biệt như "ObjectId", "Phím tối thiểu", "UUID" hoặc "MD5" (Tôi nghĩ những loại này được MongoDB yêu cầu). Các loại này không tương thích với JSON. Điều đó có nghĩa là một số thông tin loại có thể bị mất khi bạn chuyển đổi các đối tượng từ BSON sang JSON, nhưng tất nhiên chỉ khi các loại đặc biệt này có trong nguồn BSON. Việc sử dụng cả JSON và BSON trong một dịch vụ có thể là một bất lợi.

    MessagePack được thiết kế để được chuyển đổi trong suốt từ / sang JSON.

  2. MessagePack nhỏ hơn BSON

    Định dạng của MessagePack ít dài dòng hơn BSON. Kết quả là MessagePack có thể tuần tự hóa các đối tượng nhỏ hơn BSON.

    Ví dụ: một bản đồ đơn giản {"a": 1, "b": 2} được tuần tự hóa thành 7 byte với MessagePack, trong khi BSON sử dụng 19 byte.

  3. BSON hỗ trợ cập nhật tại chỗ

    Với BSON, bạn có thể sửa đổi một phần của đối tượng được lưu trữ mà không cần nối tiếp lại toàn bộ đối tượng. Giả sử bản đồ {"a": 1, "b": 2} được lưu trữ trong một tệp và bạn muốn cập nhật giá trị của "a" từ 1 đến 2000.

    Với MessagePack, 1 chỉ sử dụng 1 byte nhưng 2000 sử dụng 3 byte. Vì vậy, "b" phải được di chuyển lùi 2 byte, trong khi "b" không được sửa đổi.

    Với BSON, cả 1 và 2000 đều sử dụng 5 byte. Vì tính dài dòng này, bạn không phải di chuyển "b".

  4. MessagePack có RPC

    MessagePack, Bộ đệm giao thức, Thrift và Avro hỗ trợ RPC. Nhưng BSON thì không.

Những khác biệt này ngụ ý rằng MessagePack ban đầu được thiết kế để liên lạc qua mạng trong khi BSON được thiết kế để lưu trữ.

Thiết kế triển khai và API

  1. MessagePack có các API kiểm tra loại (Java, C ++ và D)

    MessagePack hỗ trợ gõ tĩnh.

    Gõ động được sử dụng với JSON hoặc BSON rất hữu ích cho các ngôn ngữ động như Ruby, Python hoặc JavaScript. Nhưng rắc rối cho các ngôn ngữ tĩnh. Bạn phải viết mã kiểm tra loại nhàm chán.

    MessagePack cung cấp API kiểm tra loại. Nó chuyển đổi các đối tượng gõ động thành các đối tượng gõ tĩnh. Đây là một ví dụ đơn giản (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 có IDL

    Nó liên quan đến API kiểm tra loại, MessagePack hỗ trợ IDL. (thông số kỹ thuật có sẵn từ: http://wiki.msgpack.org/display/MSGPACK/Design+of+IDL )

    Bộ đệm và tiết kiệm giao thức yêu cầu IDL (không hỗ trợ gõ động) và cung cấp triển khai IDL trưởng thành hơn.

  2. MessagePack có API phát trực tuyến (Ruby, Python, Java, C ++, ...)

    MessagePack hỗ trợ giải nén trực tuyến. Tính năng này rất hữu ích cho giao tiếp mạng. Đây là một ví dụ (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 so sánh với Google Protobufs như thế nào về kích thước dữ liệu và do đó, về hiệu suất không khí?
Ellis

4
Điểm đầu tiên nhấn mạnh đến việc MessagePack có khả năng byte thô không thể biểu diễn trong JSON. Vì vậy, nó cũng giống như BSON về vấn đề đó ...

4
@lttlrck Nói chung, các byte thô được giả sử là một chuỗi (thường là utf-8), trừ khi có dự kiến ​​và đồng ý ở cả hai phía của kênh. Trình thông báo được sử dụng như một định dạng luồng / tuần tự hóa ... và ít dài dòng hơn json .. mặc dù cũng ít người đọc hơn.
Tracker1

4
"MessagePack có API kiểm tra loại. BSON không." Không hoàn toàn chính xác. Điều này thực sự đúng đối với việc triển khai BSON bằng các ngôn ngữ được nhập tĩnh.
Brandon Black

1
MessagePack hiện có kiểu dữ liệu BINary, do đó, đối số tương thích khử tuần tự 1-1 với JSON không còn hoàn toàn đúng nữa.
zimbatm

16

Tôi biết rằng câu hỏi này hơi lạc hậu vào thời điểm này ... Tôi nghĩ rằng rất quan trọng để đề cập rằng nó phụ thuộc vào môi trường máy khách / máy chủ của bạn trông như thế nào.

Nếu bạn truyền byte nhiều lần mà không kiểm tra, chẳng hạn như với hệ thống hàng đợi tin nhắn hoặc truyền các mục nhật ký vào đĩa, thì bạn cũng có thể thích mã hóa nhị phân để nhấn mạnh kích thước nhỏ gọn. Mặt khác, nó là một vấn đề theo từng trường hợp với các môi trường khác nhau.

Một số môi trường có thể có tuần tự hóa và giải tuần tự hóa rất nhanh đến / từ trình tin của tệp packpack / protobuf, một số khác thì không nhiều. Nói chung, ngôn ngữ / môi trường càng thấp thì tuần tự nhị phân tốt hơn sẽ hoạt động. Trong các ngôn ngữ cấp cao hơn (node.js, .Net, JVM), bạn sẽ thường thấy rằng tuần tự hóa JSON thực sự nhanh hơn. Khi đó, câu hỏi đặt ra là mạng của bạn bị hạn chế nhiều hay ít hơn bộ nhớ / cpu của bạn?

Liên quan đến bộ đệm thông tin và bộ đệm giao thức so với bộ đệm giao thức ... Trình tin gói là ít byte nhất của nhóm, bộ đệm giao thức là như nhau. BSON định nghĩa các loại bản địa rộng hơn so với hai loại kia và có thể phù hợp hơn với chế độ đối tượng của bạn, nhưng điều này làm cho nó dài dòng hơn. Bộ đệm giao thức có lợi thế là được thiết kế để truyền phát ... điều này làm cho nó có định dạng tự nhiên hơn cho định dạng chuyển / lưu trữ nhị phân.

Cá nhân, tôi sẽ nghiêng về tính minh bạch mà JSON cung cấp trực tiếp, trừ khi có nhu cầu rõ ràng về lưu lượng truy cập nhẹ hơn. Qua HTTP với dữ liệu được nén, sự khác biệt về chi phí mạng thậm chí còn ít hơn một vấn đề giữa các định dạng.


6
MsgPack bản địa chỉ hiệu quả với ProtocolBuffers có kích thước khôn ngoan vì độ dài của các khóa (luôn là văn bản hiện diện) ngắn như "a" hoặc "b" - hoặc nói cách khác là một phần không đáng kể của toàn bộ tải trọng . Chúng luôn ngắn trong ProtocolBuffers sử dụng IDL / biên dịch để ánh xạ các mô tả trường thành id. Đây cũng là điều khiến MsgPack trở nên "năng động", mà ProtocolBuffers chắc chắn là không ..
user2864740

2
Điểm cuối là tốt: gzip / deflate thực sự tốt đang xử lý sự dư thừa của các khóa trong trường hợp các khóa đó "dài hơn nhưng lặp đi lặp lại nhiều lần" (MsgPack, JSON / BSON và XML, v.v. qua nhiều bản ghi) nhưng sẽ không giúp ích ProtocolBuffers ở đây .. Avro thực hiện loại bỏ dự phòng chính bằng cách truyền riêng lược đồ.
dùng2864740

4

Kiểm tra nhanh cho thấy JSON được rút gọn được khử lưu trữ nhanh hơn MessagePack nhị phân. Trong các thử nghiệm Article.json là JSON tối thiểu 550kb, Article.mpack là phiên bản MP 420kb của nó. Có thể là một vấn đề thực hiện tất nhiên.

Gói tin nhắn:

//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);
}

Vì vậy, thời gian là:

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

Vì vậy, không gian được tiết kiệm, nhưng nhanh hơn? Không.

Phiên bản thử nghiệm:

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

7
Chắc chắn phụ thuộc vào việc thực hiện. Các thử nghiệm của tôi với Python 2.7.3 giải nén 489K test.json (test.msgpack tương đương 409K) cho thấy trong 10.000 lần lặp simplejson2.6.2 mất 66,7 giây và msgpack0,2,2 chỉ mất 28,8.
Ngày

2
Article.json này đến từ đâu?
Ant6n

folks, mã kiểm tra là trong nhận xét của tôi ở trên, bạn còn mong đợi gì nữa, Article.json là đối tượng nối tiếp json từ dự án của chúng tôi. Và đến bây giờ những kết quả đó có thể không liên quan
Oleksiy Khilkevich

14
Đây không phải là một so sánh hiệu năng công bằng, vì JS có JSON được triển khai tự nhiên trong C ++, trong khi tệp tin trong JS.
Alex Panchenko

2
Bạn đang cố gắng để MessagePack nói tiếng Latin tốt hơn tiếng La Mã. JSON là nguồn gốc (C ++) cho JavaScript trong khi MessagePack được viết bằng JavaScript, được diễn giải. Về cơ bản, đây là so sánh hai đoạn mã, một đoạn được viết bằng JavaScript và đoạn mã khác được viết bằng C ++.
Ramazan Polat

0

Một sự khác biệt chính chưa được đề cập là BSON chứa thông tin kích thước tính theo byte cho toàn bộ tài liệu và các tài liệu phụ được lồng thêm.

document    ::=     int32 e_list

Điều này có hai lợi ích chính cho các môi trường bị hạn chế (ví dụ: được nhúng) trong đó kích thước và hiệu suất là quan trọng.

  1. Bạn có thể kiểm tra ngay nếu dữ liệu bạn sắp phân tích đại diện cho một tài liệu hoàn chỉnh hoặc nếu bạn sẽ cần yêu cầu nhiều hơn tại một số điểm (có thể là từ một số kết nối hoặc lưu trữ). Vì đây rất có thể là một hoạt động không đồng bộ, bạn có thể đã gửi một yêu cầu mới trước khi phân tích cú pháp.
  2. Dữ liệu của bạn có thể chứa toàn bộ tài liệu phụ với thông tin không liên quan cho bạn. BSON cho phép bạn dễ dàng di chuyển đến đối tượng tiếp theo qua tài liệu phụ bằng cách sử dụng thông tin kích thước của tài liệu phụ để bỏ qua nó. mặt khác, gói tin chứa số lượng phần tử bên trong cái được gọi là bản đồ (tương tự như tài liệu phụ của BSON). Mặc dù đây chắc chắn là thông tin hữu ích nhưng nó không giúp ích cho trình phân tích cú pháp. Bạn vẫn phải phân tích từng đối tượng trong bản đồ và không thể bỏ qua nó. Tùy thuộc vào cấu trúc dữ liệu của bạn, điều này có thể có tác động rất lớn đến hiệu suất.

0

Tôi đã thực hiện điểm chuẩn nhanh để so sánh tốc độ mã hóa và giải mã của MessagePack so với BSON. BSON nhanh hơn ít nhất nếu bạn có mảng nhị phân lớn:

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

Sử dụng C # Newtonsoft.Json và MessagePack của 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

Vâng, như tác giả đã nói , MessagePack ban đầu được thiết kế để liên lạc qua mạng trong khi BSON được thiết kế để lưu trữ.

MessagePack nhỏ gọn trong khi BSON dài dòng. MessagePack có nghĩa là tiết kiệm không gian trong khi BSON được thiết kế cho CURD (tiết kiệm thời gian).

Quan trọng nhất, hệ thống loại của MessagePack (tiền tố) tuân theo mã hóa Huffman, ở đây tôi đã vẽ một cây Huffman của MessagePack (nhấp vào liên kết để xem hình ảnh)

Cây thông điệp Huffman

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.