Серіалізація суб'єкта господарювання: BSON vs MessagePack (проти JSON)


137

Нещодавно я знайшов MessagePack , альтернативний формат бінарної серіалізації для буферів протоколів Google та JSON, який також перевершує обидва.

Також є формат серіалізації BSON , який використовується MongoDB для зберігання даних.

Чи може хтось розглянути відмінності та недоліки BSON проти MessagePack ?


Просто для заповнення списку ефективних форматів бінарної серіалізації: Є також Gobs, які стануть наступником буферів протоколів Google . Однак на відміну від усіх інших згаданих форматів, вони не є мовними агностиками і покладаються на вбудовану рефлексію Go, є також бібліотеки Gobs принаймні на іншій мові, ніж Go.


3
Здається, це як навантаження на маркетинговий ажіотаж. Продуктивність формату серіалізації ["складеного"] обумовлена ​​використовуваною реалізацією. Хоча деякі формати мають по суті більше накладних витрат (наприклад, JSON, оскільки це все динамічно обробляється), самі формати "не мають швидкості". Потім сторінка переходить до "вибору і вибору", як вона порівнює себе ... це дуже непредвзята мода. Не моя чашка чаю.

6
Виправлення: Gobs не призначені для заміни буферів протоколів, і, ймовірно, ніколи не будуть. Також Gobs є агностиком мови (їх можна читати / писати будь-якою мовою, див. Code.google.com/p/libgob ), але вони визначені так, щоб вони відповідали тому, як Go поводиться з даними, тому вони найкраще працюють з Go.
Кайл С

6
Посилання на показники ефективності msgpack порушено ( msgpack.org/index/speedtest.png ).
Аліаксей Раманау

Відповіді:


197

// Зверніть увагу, що я автор MessagePack. Ця відповідь може бути упередженою.

Формат дизайну

  1. Сумісність з JSON

    Незважаючи на свою назву, сумісність BSON з JSON не настільки хороша в порівнянні з MessagePack.

    BSON має спеціальні типи, такі як "ObjectId", "Мін ключ", "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, буфери протоколів, ощадливість та 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 )

    Буферні протоколи та ощадливість вимагають IDL (не підтримують динамічне введення тексту) та забезпечують більш зрілу реалізацію IDL.

  2. MessagePack має потоковий API (Ruby, Python, Java, C ++, ...)

    MessagePack підтримує потокові десеріалізатори. Ця функція корисна для мережевого спілкування. Ось приклад (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 за розміром даних, а отже, і за ефективністю в ефірі?
Елліс

4
Перша точка полягає в тому, що MessagePack має необмежену кількість байтів, яка не може бути представлена ​​в JSON. Таким чином, це точно так само, як BSON в цьому плані ...

4
@lttlrck Як правило, необроблені байти вважаються рядком (як правило, utf-8), якщо інше не очікується і погоджено з обох боків каналу. msgpack використовується як формат потоку / серіалізації ... і менш багатослівний, що json .., хоча і менш читабельний.
Tracker1

4
"MessagePack має API перевірки типу. BSON не робить." Не зовсім точна. Це насправді справедливо і для реалізацій BSON на статично типових мовах.
Брендон Блек

1
MessagePack тепер має тип даних BINARY, тому аргумент сумісності 1-1 десеріалізації до JSON вже не зовсім вірний.
zimbatm

16

Я знаю, що це питання трохи датоване на даний момент ... Я думаю, що це дуже важливо згадати, що це залежить від того, як виглядає середовище вашого клієнта / сервера.

Якщо ви передаєте байти кілька разів без перевірки, наприклад, із системою черги повідомлень або потоковими записами журналу на диск, то ви можете віддати перевагу двійковому кодуванню, щоб підкреслити компактний розмір. В іншому випадку це стосується конкретного випадку в різних середовищах.

Деякі середовища можуть мати дуже швидку серіалізацію та десеріалізацію до / з msgpack / protobuf, інші - не так вже й багато. Загалом, чим нижчий рівень мови / середовища, тим краще працюватиме двійкова серіалізація. У мовах вищого рівня (node.js, .Net, JVM) ви часто бачите, що серіалізація JSON насправді швидша. Тоді виникає питання, чи ваша мережа накладних витрат більш-менш обмежена, ніж ваша пам'ять / процесор?

Що стосується msgpack vs bson vs протокольних буферів ... msgpack - найменший байт групи, буфери протоколів приблизно однакові. BSON визначає більш широкі натурні типи, ніж інші два, і може краще відповідати вашому об'єктовому режиму, але це робить його більш багатослівним. Буфери протоколів мають перевагу в тому, що вони розроблені для передачі потоку ..., що робить його більш природним форматом для бінарного формату передачі / зберігання.

Особисто я б схилявся до прозорості, яку пропонує безпосередньо JSON, якщо тільки немає чіткої потреби у більш легкому русі. У HTTP із gzipped даними різниця між накладними витратами на мережу є ще меншою проблемою між форматами.


6
Native MsgPack ефективний лише з розміром ProtocolBuffers, оскільки довжина клавіш (які завжди присутні в тексті) є короткими, наприклад "a" або "b" - або в іншому випадку є незначною частиною всього корисного навантаження . Вони завжди не вистачають у ProtocolBuffers, який використовує IDL / компіляцію для відображення дескрипторів поля на ідентифікатори. Це також робить MsgPack "динамічним", чого, звичайно, не було.
user2864740

2
Кінцева точка хороша, хоча: gzip / deflate дійсно добре, вони обробляють надмірність клавіш у випадках, коли такі клавіші "довші, але багато разів повторюються" (MsgPack, JSON / BSON, і XML тощо тощо у багатьох записах), але не допоможуть Тут взагалі протокольні буфери. Avro виконує усунення надмірності вручну вручну, передаючи схему окремо.
користувач2864740

4

Швидкий тест показує, що мінімізований JSON дезаріалізується швидше, ніж двійковий MessagePack. У тестах Article.json - це 550kb мінімізований JSON, Article.mpack - 420 кбіт MP-версія. Звичайно, це може бути питання щодо впровадження.

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, розпаковуючи test.json 489K (еквівалент 409K test.msgpack), показують, що для 10 000 ітерацій simplejson2.6.2 потрібно 66.7 секунд, а msgpack0.2.2 - всього 28.8.
День

2
Звідки взявся цей Article.json?
Ant6n

люди, тестовий код є в моєму коментарі вище, що ще ви очікували, Article.json - серійний об'єкт json з нашого проекту. І на даний момент ці результати в будь-якому разі можуть бути неактуальними
Олексій Хількевич

14
Це не справедливе порівняння продуктивності, оскільки JS JSON реалізується на C ++, тоді як msgpack в JS.
Олексій Панченко

2
Ви намагаєтеся змусити MessagePack говорити латині краще, ніж римляни. JSON є рідною (C ++) для JavaScript, тоді як MessagePack написаний на JavaScript, що інтерпретується. Це в основному порівняння двох фрагментів коду, один написаний на JavaScript, а інший написаний на C ++.
Рамазан Полат

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 є багатослівним. MessagePack призначений для використання в просторі, тоді як BSON розроблений для CURD (ефективність у часі).

Найголовніше, що система типу (префікс) MessagePack дотримується кодування Хаффмана, тут я намалював дерево Хаффмана MessagePack (натисніть посилання, щоб побачити зображення):

Хаффман Дерево повідомлення

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.