web-dev-qa-db-fra.com

Sérialisation d'entité performante: BSON vs MessagePack (vs JSON)

Récemment, j'ai trouvé MessagePack, une alternative Format de sérialisation binaire sur les Protocol Buffers et JSON de Google, qui surpasse également les deux.

Il existe également le format de sérialisation BSON utilisé par MongoDB pour stocker les données.

Quelqu'un peut-il élaborer les différences et les inconvénients/avantages de BSON par rapport à MessagePack?


Juste pour compléter la liste des formats de sérialisation binaires performants: Il y a aussi Gobs qui vont être le successeur des tampons de protocole de Google. toutefois contrairement à tous les autres formats mentionnés, ceux-ci ne sont pas indépendants de la langue et reposent sur réflexion intégrée de Go il existe également des bibliothèques Gobs pour au moins une autre langue que Go.

130
Alex

// Veuillez noter que je suis l'auteur de MessagePack. Cette réponse peut être biaisée.

Conception du format

  1. Compatibilité avec JSON

    Malgré son nom, la compatibilité de BSON avec JSON n'est pas aussi bonne comparée à MessagePack.

    BSON possède des types spéciaux tels que "ObjectId", "Min key", "UUID" ou "MD5" (je pense que ces types sont requis par MongoDB). Ces types ne sont pas compatibles avec JSON. Cela signifie que certaines informations de type peuvent être perdues lorsque vous convertissez des objets de BSON en JSON, mais bien entendu uniquement lorsque ces types spéciaux se trouvent dans la source BSON. Il peut être désavantageux d'utiliser à la fois JSON et BSON dans un seul service.

    MessagePack est conçu pour être converti de manière transparente de/vers JSON.

  2. MessagePack est plus petit que BSON

    Le format de MessagePack est moins détaillé que BSON. En conséquence, MessagePack peut sérialiser des objets plus petits que BSON.

    Par exemple, une mappe simple {"a": 1, "b": 2} est sérialisée sur 7 octets avec MessagePack, tandis que BSON utilise 19 octets.

  3. BSON prend en charge la mise à jour sur place

    Avec BSON, vous pouvez modifier une partie de l'objet stocké sans re-sérialiser l'ensemble de l'objet. Supposons qu'une carte {"a": 1, "b": 2} soit stockée dans un fichier et que vous souhaitiez mettre à jour la valeur de "a" de 1 à 2000.

    Avec MessagePack, 1 utilise seulement 1 octet mais 2000 utilise 3 octets. Donc, "b" doit être reculé de 2 octets, tandis que "b" n'est pas modifié.

    Avec BSON, 1 et 2000 utilisent 5 octets. En raison de cette verbosité, vous n'avez pas besoin de déplacer "b".

  4. MessagePack a RPC

    MessagePack, Protocol Buffers, Thrift et Avro prennent en charge le protocole RPC. Mais pas BSON.

Ces différences impliquent que MessagePack est conçu à l'origine pour la communication réseau, tandis que BSON est conçu pour les stockages.

Implémentation et conception de l'API

  1. MessagePack a des API de vérification de type (Java, C++ et D)

    MessagePack prend en charge le typage statique.

    Le typage dynamique utilisé avec JSON ou BSON est utile pour les langages dynamiques tels que Ruby, Python ou JavaScript. Mais pénible pour les langages statiques. Vous devez écrire des codes de vérification de type ennuyeux.

    MessagePack fournit une API de vérification de type. Il convertit les objets à typage dynamique en objets à typage statique. Voici un exemple simple (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 a IDL

    En relation avec l'API de vérification de type, MessagePack prend en charge IDL. (la spécification est disponible à partir de: http://wiki.msgpack.org/display/MSGPACK/Design+of+IDL )

    Les tampons de protocole et Thrift nécessitent un IDL (ne prennent pas en charge le typage dynamique) et fournissent une implémentation IDL plus mature.

  2. MessagePack a une API de streaming (Ruby, Python, Java, C++, ...)

    MessagePack prend en charge les désérialiseurs en continu. Cette fonctionnalité est utile pour la communication réseau. Voici un exemple (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
    }
181
Sadayuki Furuhashi

Je sais que cette question est un peu dépassée à ce stade ... Je pense qu'il est très important de mentionner que cela dépend de votre environnement client/serveur.

Si vous passez plusieurs fois des octets sans inspection, par exemple avec un système de file d'attente de messages ou la transmission en continu d'entrées de journal sur disque, vous préférerez peut-être un codage binaire pour souligner la taille compacte. Sinon, c'est un problème au cas par cas avec différents environnements.

Certains environnements peuvent avoir une sérialisation et une désérialisation très rapides vers/depuis msgpack/protobuf, d'autres moins. En général, plus la langue/l'environnement est faible, meilleure sera la sérialisation binaire. Dans les langages de niveau supérieur (node.js, .Net, JVM), vous verrez souvent que la sérialisation JSON est en réalité plus rapide. La question est alors de savoir si votre surcharge réseau est plus ou moins contrainte que votre mémoire/unité centrale de traitement.

En ce qui concerne msgpack vs bson vs tampons de protocole ... msgpack est le plus petit octet du groupe, les tampons de protocole étant à peu près les mêmes. BSON définit des types natifs plus larges que les deux autres, et correspond peut-être mieux au mode objet, mais cela le rend plus détaillé. Les tampons de protocole ont l’avantage d’être conçus pour le streaming ... ce qui en fait un format plus naturel pour un format de transfert/stockage binaire.

Personnellement, je m'appuierais sur la transparence offerte directement par JSON, à moins que le trafic ne soit clairement nécessaire. Sur HTTP avec des données compressées, la différence de surcharge réseau est encore moins problématique entre les formats.

16
Tracker1

Le test rapide montre que le JSON minifié est désérialisé plus rapidement que MessagePack binaire. Dans les tests Article.json est un fichier JSON de 550 Ko minified, Article.mpack est une version MP de 420 Ko. Peut-être un problème de mise en œuvre bien sûr.

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

Donc les temps sont:

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

Donc, l'espace est économisé, mais plus rapide? Non.

Versions testées:

Anarki:Downloads oleksii$ node --version
v0.8.12
Anarki:Downloads oleksii$ npm list msgpack
/Users/oleksii
└── [email protected]  
4

Eh bien, comme l’a dit l’auteur, MessagePack est à l’origine conçu pour la communication réseau, tandis que BSON est conçu pour les stockages.

MessagePack est compact tandis que BSON est commenté. MessagePack est censé être peu encombrant tandis que BSON est conçu pour CURD (gain de temps).

Plus important encore, le système de types de MessagePack (préfixe) suit le codage de Huffman. Ici, j'ai dessiné un arbre de MessagePack de Huffman (cliquez sur le lien pour voir l'image):

Huffman Tree of MessagePack

0
Jim

J'ai rapidement réalisé une comparaison pour comparer la vitesse d'encodage et de décodage de MessagePack par rapport à BSON. BSON est au moins plus rapide si vous avez de grands tableaux binaires:

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

Utilisation de C # Newtonsoft.Json et MessagePack de 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
itix

Une différence essentielle qui n’a pas encore été mentionnée est que BSON contient des informations sur la taille en octets pour l’ensemble du document et d’autres sous-documents imbriqués.

document    ::=     int32 e_list

Ceci présente deux avantages majeurs pour les environnements restreints (par exemple, intégrés) où la taille et les performances sont importantes.

  1. Vous pouvez immédiatement vérifier si les données que vous allez analyser représentent un document complet ou si vous allez avoir besoin d'en demander plus à un moment donné (que ce soit à partir d'une connexion ou d'un stockage). Comme il s'agit très probablement d'une opération asynchrone, il est possible que vous envoyiez déjà une nouvelle demande avant l'analyse.
  2. Vos données peuvent contenir des sous-documents entiers avec des informations non pertinentes pour vous. BSON vous permet de parcourir facilement le prochain objet après le sous-document en utilisant les informations de taille du sous-document pour le sauter. msgpack, d'autre part, contient le nombre d'éléments contenus dans ce que l'on appelle une carte (similaire aux sous-documents de BSON). Bien que ce soit sans aucun doute une information utile, cela n’aide en rien l’analyseur. Vous devez toujours analyser chaque objet de la carte et ne pas simplement le sauter. En fonction de la structure de vos données, cela peut avoir un impact considérable sur les performances.
0
Vinci