Серийная сериализация исполнителей: BSON vs MessagePack (vs JSON)

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

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

Может кто-нибудь уточнить различия и недостатки BSON vs MessagePack?


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

Ответ 1

//Обратите внимание, что я автор MessagePack. Этот ответ может быть предвзятым.

Дизайн формата

  1. Совместимость с JSON

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

    BSON имеет специальные типы, такие как "ObjectId", "Min key", "UUID" или "MD5" (я думаю, что эти типы требуются MongoDB). Эти типы не совместимы с JSON. Это означает, что некоторая информация о типах может быть потеряна при преобразовании объектов из BSON в JSON, но, конечно, только когда эти специальные типы находятся в источнике BSON. Использование JSON и BSON в одном сервисе может быть недостатком.

    MessagePack предназначен для прозрачного преобразования из/в JSON.

  2. Пакет сообщений меньше, чем 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)

    Буферы протокола и 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
    }

Ответ 2

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

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

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

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

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

Ответ 3

Быстрый тест показывает, что уменьшенный JSON десериализуется быстрее, чем двоичный MessagePack. В тестах Article.json составляет 550kb, уменьшенный JSON, Article.mpack - это 420kb 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
└── [email protected]  

Ответ 4

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

document    ::=     int32 e_list

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

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

Ответ 5

Я сделал быстрый тест для сравнения скорости кодирования и декодирования MessagePack и BSON. BSON быстрее, по крайней мере, если у вас большие двоичные массивы:

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

Использование С# 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);
    }

Ответ 6

Ну, как сказал автор, MessagePack изначально предназначен для сетевого взаимодействия, а BSON - для хранилищ.

MessagePack компактен, а BSON многословен. MessagePack предназначен для экономии пространства, а BSON разработан для CURD (экономия времени).

Самое главное, что система типов MessagePack (префикс) следует за кодировкой Хаффмана, здесь я нарисовал дерево Хаффмана из MessagePack (нажмите на ссылку, чтобы увидеть изображение):

Huffman Tree of MessagePack