执行者实体序列化:BSON与MessagePack(vs JSON)

|| 最近,我找到了MessagePack,它是Google的协议缓冲区和JSON的另一种二进制序列化格式,它的性能也优于两者。 还有MongoDB用于存储数据的BSON序列化格式。 有人可以详细说明BSON与MessagePack的区别和缺点吗? 只是为了完成高性能二进制序列化格式的列表:还有Gob,它们将成为Google的协议缓冲区的后继产品。但是,与所有其他提到的格式相反,这些格式不是语言不可知的,它们依赖于Go \的内置反射,此外还有Gobs库,至少针对Go以外的其他语言。     
已邀请:
        //请注意,我是MessagePack的作者。这个答案可能有偏见。 格式设计 与JSON的兼容性 尽管其名称,BSON与JSON的兼容性与MessagePack相比并不是很好。 BSON具有特殊类型,例如\“ ObjectId \”,\“ Min key \”,\“ UUID \”或\“ MD5 \”(我认为MongoDB需要这些类型)。这些类型与JSON不兼容。这意味着当您将对象从BSON转换为JSON时,某些类型信息可能会丢失,但是当然只有当这些特殊类型在BSON源中时,这些信息才会丢失。在单个服务中同时使用JSON和BSON可能是不利的。 MessagePack旨在透明地从JSON转换为JSON。 MessagePack小于BSON MessagePack的格式不如BSON冗长。结果,MessagePack可以序列化小于BSON的对象。 例如,一个简单的映射{\“ a \”:1,\“ b \”:2}使用MessagePack序列化为7个字节,而BSON使用19个字节。 BSON支持就地更新 使用BSON,您可以修改存储对象的一部分,而无需重新序列化整个对象。假设映射{\“ a \”:1,\“ b \”:2}存储在文件中,并且您想将\“ a \”的值从1更新为2000。 对于MessagePack,1仅使用1个字节,而2000使用3个字节。因此\“ b \”必须向后移2个字节,而\“ b \”不能修改。 使用BSON,1和2000都使用5个字节。由于这种冗长,您不必移动\“ b \”。 MessagePack具有RPC MessagePack,协议缓冲区,节流和Avro支持RPC。但是BSON不会。 这些差异意味着MessagePack最初是为网络通信而设计的,而BSON是为存储而设计的。 实施和API设计 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>();
    }
MessagePack具有IDL 它与类型检查API有关,MessagePack支持IDL。 (可以从以下网址获得规范:http://wiki.msgpack.org/display/MSGPACK/Design+of+IDL) 协议缓冲区和节流协议需要IDL(不支持动态类型),并提供更成熟的IDL实现。 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
    }
    
我知道这个问题在这一点上有点过时了……我认为提到这取决于您的客户机/服务器环境是非常重要的。 如果您不检查就多次传递字节,例如使用消息队列系统或将日志条目流传输到磁盘,那么您可能更喜欢二进制编码来强调紧凑的大小。否则,在不同的环境下会因情况而异。 某些环境可以在msgpack / protobuf中进行非常快速的序列化和反序列化,而其他环境则没有。通常,语言/环境的级别越低,二进制序列化越好。在高级语言(node.js,.Net,JVM)中,您通常会看到JSON序列化实际上更快。那么问题就变成了您的网络开销比您的内存/ cpu受到更多或更少的约束吗? 关于msgpack vs bson vs协议缓冲区... msgpack是该组中最小的字节,协议缓冲区大致相同。 BSON比其他两种定义了更广泛的本机类型,并且可能与您的对象模式更好地匹配,但这使它更加冗长。协议缓冲区具有被设计为流式传输的优点……这使其成为二进制传输/存储格式的更自然的格式。 就个人而言,除非明确需要减少流量,否则我将倾向于JSON直接提供的透明度。通过带有压缩数据的HTTP,格式之间的网络开销差异甚至不再是问题。     
        快速测试显示,与二进制MessagePack相比,缩小的JSON反序列化的速度更快。在测试中,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
└── msgpack@0.1.7  
    
        尚未提及的主要区别是BSON包含整个文档和其他嵌套子文档的大小信息(以字节为单位)。
document    ::=     int32 e_list
对于大小和性能很重要的受限环境(例如嵌入式),这有两个主要好处。 您可以立即检查要解析的数据是否代表完整的文档,或者是否需要在某个时候(从某个连接或存储中)请求更多数据。由于这很可能是异步操作,因此您可能已经在解析之前发送了新请求。 您的数据可能包含带有不相关信息的整个子文档。通过BSON,您可以通过使用子文档的大小信息跳过该文档,轻松遍历子文档中的下一个对象。另一方面,msgpack包含所谓映射中的元素数量(类似于BSON的子文档)。尽管这无疑是有用的信息,但对解析器没有帮助。您仍然必须解析地图中的每个对象,而不能只是跳过它。根据数据的结构,这可能会对性能产生巨大影响。     
        我做了一个快速基准测试,比较了MessagePack和BSON的编码和解码速度。至少在具有大型二进制数组的情况下,BSON更快:
BSON writer: 2296 ms (243487 bytes)
BSON reader: 435 ms
MESSAGEPACK writer: 5472 ms (243510 bytes)
MESSAGEPACK reader: 1364 ms
使用neuecc的C#Newtonsoft.Json和MessagePack:
    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);
    }
    
        好吧,正如作者所说,MessagePack最初是为网络通信而设计的,而BSON是为存储而设计的。 MessagePack是紧凑的,而BSON是冗长的。 MessagePack旨在节省空间,而BSON专为CURD设计(省时)。 最重要的是,MessagePack的类型系统(前缀)遵循霍夫曼编码,在这里我画了一个霍夫曼树的MessagePack(单击链接查看图片):     

要回复问题请先登录注册