高性能实体序列化:BSON vs MessagePack(vs JSON)

最近我发现了MessagePack ,它是Google 协议缓冲区和JSON的另一种二进制序列化格式。

还有MongoDB用来存储数据的BSON序列化格式。

有人能详细说明BSON与MessagePack区别和优缺点吗?


只是为了完成性能二进制序列化格式的列表:还有Gobs 将成为谷歌的协议缓冲区的继任者 。 然而,与所有其他提及的格式相比,这些格式不是语言不可知的,依靠Go的内置reflection ,至less在Go语言之外还有Gobs库。

//请注意,我是MessagePack的作者。 这个答案可能是有偏见的。

格式devise

  1. 与JSON兼容

    尽pipe名称不同,BSON与JSON的兼容性不如MessagePack好。

    BSON有“ObjectId”,“Min key”,“UUID”或“MD5”(我认为这些types是MongoDB所要求的)的特殊types。 这些types与JSON不兼容。 这意味着某些types信息在将对象从BSON转换为JSON时会丢失。 在单一服务中使用JSON和BSON可能是不利的。

    MessagePack被devise为透明地从/转换成JSON。

  2. MessagePack比BSON小

    MessagePack的格式不比BSON冗长。 结果,MessagePack可以序列化小于BSON的对象。

    例如,一个简单的映射{“a”:1,“b”:2}与MessagePack以7个字节被序列化,而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最初是为networking通信而devise的,而BSON是为存储devise的。

实施和APIdevise

  1. MessagePack具有types检查API(Java,C ++和D)

    MessagePack支持静态input。

    用于JSON或BSON的dynamictypes对于Ruby,Python或JavaScript等dynamic语言非常有用。 但是对于静态语言来说很麻烦。 你必须写无聊的types检查代码。

    MessagePack提供了types检查API。 它将dynamictypes的对象转换成静态types的对象。 这是一个简单的例子(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>(); } 
  2. MessagePack有IDL

    它与types检查API有关,MessagePack支持IDL。 (规范可以从http://wiki.msgpack.org/display/MSGPACK/Design+of+IDL获得; )

    Protocol Buffers和Thrift需要IDL(不支持dynamicinput),并提供更成熟的IDL实现。

  3. MessagePack有streamAPI(Ruby,Python,Java,C ++,…)

    MessagePack支持stream解串器。 此function对于networking通信非常有用。 这里是一个例子(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 } 

我知道这个问题在这一点上有点过时了……我认为提及它取决于你的客户/服务器环境是非常重要的。

如果您多次传送字节而未经检查(例如使用消息队列系统或将stream日志条目传送到磁盘),那么您可能更喜欢使用二进制编码来强调紧凑的大小。 否则,这是一个在不同环境下的案例问题。

有些环境可以非常快速地从msgpack / protobuf中进行序列化和反序列化,而另一些则不是那么多。 一般来说,更低层次的语言/环境,更好的二进制序列化将起作用。 在更高级别的语言(node.js,.Net,JVM)中,您经常会看到JSON序列化实际上更快。 那么问题就是你的networking开销比你的内存/ CPU多less受到一些限制?

关于msgpack vs bson vs协议缓冲区… msgpack是组的最小字节数,协议缓冲区大致相同。 BSON比另外两种定义了更广泛的本地types,可能会更好地匹配你的对象模式,但是这使得它更加冗长。 协议缓冲区的优点是被devise为stream…这使得它成为二进制传输/存储格式更自然的格式。

就个人而言,我会倾向于JSON直接提供的透明度,除非明确需要更轻的stream量。 通过使用gzip压缩的数据的HTTP,networking开销的差异更不是格式之间的问题。

快速testing显示缩小的JSON反序列化比二进制MessagePack更快。 在testing中,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 

所以节省空间,但更快? 没有。

testing版本:

 Anarki:Downloads oleksii$ node --version v0.8.12 Anarki:Downloads oleksii$ npm list msgpack /Users/oleksii └── msgpack@0.1.7