Сериализация Entity Entity: BSON против MessagePack (против JSON) - PullRequest
128 голосов
/ 15 июня 2011

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

Также существует формат сериализации BSON , который используется MongoDB для хранения данных.

Может кто-нибудь уточнить различия и недостатки / преимущества BSON против MessagePack ?


Просто для того, чтобы завершить список эффективных форматов двоичной сериализации: Есть также Gobs , которые станут преемниками протокольных буферов Google . Однако в отличие от всех других упомянутых форматов, которые не зависят от языка и основаны на встроенном отражении Go , есть также библиотеки Gobs по крайней мере на другом языке, кроме Go.

Ответы [ 5 ]

179 голосов
/ 15 июня 2011

// Обратите внимание, что я являюсь автором 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, протоколы буферов, 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)

    Буферы протокола и Thrift требуют 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
    }
16 голосов
/ 31 июля 2013

Я знаю, что этот вопрос немного устарел на данный момент ... Я думаю, очень важно упомянуть, что это зависит от того, как выглядит ваша клиент-серверная среда.

Если вы передаете байты несколько раз без проверки, например, с помощью системы очереди сообщений или потоковой передачи записей журнала на диск, тогда вы можете предпочесть двоичную кодировку, чтобы подчеркнуть компактный размер. В противном случае это проблема для каждого отдельного случая.

Некоторые среды могут иметь очень быструю сериализацию и десериализацию в / из msgpack / protobuf, другие не так много. В целом, чем ниже уровень языка / среды, тем лучше будет двоичная сериализация. В языках более высокого уровня (node.js, .Net, JVM) вы часто увидите, что сериализация JSON на самом деле быстрее. Тогда возникает вопрос, является ли нагрузка на вашу сеть более или менее ограниченной, чем ваша память / процессор?

Что касается msgpack против bson против протокольных буферов ... msgpack - это наименьшее количество байтов в группе, причем протокольные буферы примерно одинаковы. BSON определяет более широкие собственные типы, чем два других, и может лучше соответствовать вашему объектному режиму, но это делает его более многословным. Протоколные буферы имеют то преимущество, что они предназначены для потоковой передачи ... что делает их более естественным форматом для двоичного формата передачи / хранения.

Лично я бы склонялся к прозрачности, которую предлагает JSON напрямую, если нет явной необходимости в меньшем трафике. По HTTP с сжатыми данными разница в затратах сети является еще меньшей проблемой между форматами.

4 голосов
/ 06 ноября 2012

Быстрый тест показывает, что минимизированный JSON десериализуется быстрее, чем двоичный MessagePack.В тестах Article.json - это минимизированный JSON 550 Кбайт, Article.mpack - это MP-версия 420 Кбайт.Конечно, это может быть проблема с реализацией.

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  
0 голосов
/ 29 апреля 2019

Я сделал быстрый тест для сравнения скорости кодирования и декодирования 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 голосов
/ 03 апреля 2019

Ключевое отличие, которое еще не упоминалось, заключается в том, что BSON содержит информацию о размере в байтах для всего документа и других вложенных вложенных документов.

document    ::=     int32 e_list

Это имеет два основных преимущества для ограниченных сред (например, встроенных), гдеважен размер и производительность.

  1. Вы можете сразу проверить, представляют ли данные, которые вы собираетесь анализировать, полный документ или вам нужно будет запросить больше в какой-то момент (будь то изкакое-то соединение или хранилище).Поскольку это, скорее всего, асинхронная операция, перед синтаксическим анализом вы уже можете отправить новый запрос.
  2. Ваши данные могут содержать целые поддокументы с несущественной для вас информацией.BSON позволяет легко переходить к следующему объекту после вложенного документа, используя информацию о размере вложенного документа, чтобы пропустить его.msgpack, с другой стороны, содержит количество элементов внутри так называемой карты (аналогично поддокументам BSON).Хотя это, несомненно, полезная информация, она не помогает парсеру.Вам все равно придется анализировать каждый объект внутри карты, и вы не можете просто пропустить его.В зависимости от структуры ваших данных это может оказать огромное влияние на производительность.
...