Сериализация Entity Entity: BSON против MessagePack (против JSON)

137

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

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

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


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

Alex
источник
3
Похоже, в основном как маркетинговый обман. Производительность ["скомпилированного"] формата сериализации зависит от используемой реализации. Хотя некоторые форматы по своей природе имеют больше накладных расходов (например, JSON, поскольку все они динамически обрабатываются), сами форматы не имеют «скорости». Затем страница «выбирает и выбирает», как она себя сравнивает ... это очень непредвзято. Не моя чашка чая.
6
Исправление: Гобы не предназначены для замены протокольных буферов и, вероятно, никогда не будут. Кроме того, Gobs не зависят от языка (их можно читать / писать на любом языке, см. Code.google.com/p/libgob ), но они определены так, чтобы точно соответствовать тому, как Go работает с данными, поэтому они лучше всего работают с Go.
Кайл С
6
Ссылка на тесты производительности msgpack не работает ( msgpack.org/index/speedtest.png ).
Алексей Раманау

Ответы:

197

// Обратите внимание, что я автор 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 байтов. Из-за этого многословия вам не нужно двигать «б».

  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
    }
Садаюки Фурухаси
источник
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 больше не является полностью верным.
Зимбатм
16

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

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

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

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

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

Tracker1
источник
6
Собственный MsgPack эффективен только с размерами ProtocolBuffers, так как длина ключей (которые всегда присутствуют в тексте) короткие, такие как «a» или «b», или иным образом являются незначительной частью всей полезной нагрузки . Они всегда коротки в ProtocolBuffers, который использует IDL / compile для сопоставления дескрипторов полей с идентификаторами. Это также то, что делает MsgPack «динамическим», которого, скорее всего, нет ProtocolBuffers ..
user2864740
2
Хотя конечная точка хороша: gzip / deflate действительно хороши для обработки избыточности ключей в тех случаях, когда такие ключи «длиннее, но многократно» (MsgPack, JSON / BSON и XML и т. Д. Для многих записей), но не помогут ProtocolBuffers вообще здесь. Avro выполняет устранение избыточности ключей вручную, передавая схему отдельно.
user2864740 26.02.15
4

Быстрый тест показывает, что минимизированный 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  
Алексей Хилькевич
источник
7
Определенно зависит от реализации. Мои тесты с Python 2.7.3, распаковывающие 489K test.json (эквивалент 409K test.msgpack), показывают, что для 10000 итераций 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). Хотя это, несомненно, полезная информация, она не помогает парсеру. Вам все равно придется анализировать каждый объект внутри карты, и вы не можете просто пропустить его. В зависимости от структуры ваших данных это может оказать огромное влияние на производительность.
Vinci
источник
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);
    }
itix
источник
0

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

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

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

Хаффман Дерево MessagePack

Джим
источник