수행자 엔티티 직렬화 : BSON vs MessagePack (vs JSON)


137

최근 에 Google의 프로토콜 버퍼JSON에 대한 대체 이진 직렬화 형식 인 MessagePack을 발견 했습니다.

또한 MongoDB가 데이터를 저장하는 데 사용 하는 BSON 직렬화 형식이 있습니다.

누군가 BSON과 MessagePack차이점과 단점을 자세히 설명 할 수 있습니까 ?


실행 가능한 바이너리 직렬화 형식의 목록을 작성하기 만하면됩니다 . Google의 프로토콜 버퍼의 후속 버전이 될 Gob있습니다 . 그러나 언급 된 다른 모든 형식과 달리 언어에 구애받지 않으며 Go의 내장 리플렉션 에 의존 하며 적어도 Go 이외의 다른 언어에 대한 Gobs 라이브러리도 있습니다.


3
대부분의 마케팅 과대 광고처럼 보입니다. [ "컴파일 된"] 직렬화 형식의 성능은 사용 된 구현으로 인한 것입니다. 일부 형식은 본질적으로 오버 헤드가 더 많지만 (예 : JSON은 모두 동적으로 처리되므로) 형식 자체는 "속도"가 없습니다. 그런 다음 페이지는 어떻게 자신을 비교하고 "매우 편견없는 방식"을 선택하고 선택합니다. 내 차가 아니야

6
수정 : Gob는 프로토콜 버퍼를 대체하기위한 것이 아니며 절대로 사용하지 않을 것입니다. 또한 Gobs는 언어에 구애받지 않으며 (어떤 언어로도 읽고 쓸 수 있으며 code.google.com/p/libgob 참조 ) Go는 데이터를 처리하는 방식과 밀접하게 일치하도록 정의되어 Go와 가장 잘 작동합니다.
Kyle C

6
msgpack 성능 벤치 마크에 대한 링크가 끊어졌습니다 ( msgpack.org/index/speedtest.png ).
Aliaksei Ramanau

답변:


197

// MessagePack의 저자입니다. 이 답변은 편향 될 수 있습니다.

포맷 디자인

  1. JSON과의 호환성

    이름에도 불구하고 BSON의 JSON과의 호환성은 MessagePack에 비해 그리 좋지 않습니다.

    BSON에는 "ObjectId", "Min key", "UUID"또는 "MD5"와 같은 특수 유형이 있습니다 (MongoDB에서 이러한 유형이 필요하다고 생각합니다). 이 유형은 JSON과 호환되지 않습니다. 즉, BSON에서 JSON으로 객체를 변환 할 때 일부 유형 정보가 손실 될 수 있지만 물론 이러한 특수 유형이 BSON 소스에있는 경우에만 해당됩니다. 단일 서비스에서 JSON과 BSON을 모두 사용하는 것은 단점이 될 수 있습니다.

    MessagePack은 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, 프로토콜 버퍼, 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 프로토 타입과 어떻게 비교됩니까?
엘리스

4
첫 번째 요점은 MessagePack에 원시 바이트 기능이 있으며 이는 JSON으로 표현할 수 없다는 사실에 대한 것입니다. 따라서 그 점에서 BSON과 동일합니다 ...

4
@lttlrck 일반적으로 원시 바이트는 달리 예상되고 채널의 양쪽에서 합의되지 않는 한 문자열 (일반적으로 utf-8) 인 것으로 가정합니다. msgpack은 스트림 / 직렬화 형식으로 사용되며 json은 덜 장황하지만 사람이 읽을 수는 없습니다.
Tracker1

4
"MessagePack에는 유형 확인 API가 있습니다. BSON은 없습니다." 완전히 정확한 것은 아닙니다. 이것은 정적으로 형식이 지정된 언어로 BSON을 구현할 때도 마찬가지입니다.
Brandon Black

1
MessagePack은 이제 BINARY 데이터 유형을 가지므로 JSON에 대한 1-1 직렬화 해제 호환성의 인수가 더 이상 사실이 아닙니다.
zimbatm

16

나는이 질문이이 시점에서 약간 날짜가 있다는 것을 알고있다. 나는 그것이 당신의 클라이언트 / 서버 환경이 어떻게 생겼는지에 달려 있다는 것을 언급하는 것이 매우 중요하다고 생각한다.

메시지 큐 시스템이나 로그 항목을 디스크로 스트리밍하는 등 검사없이 바이트를 여러 번 전달하는 경우 컴팩트 한 크기를 강조하기 위해 이진 인코딩을 선호 할 수 있습니다. 그렇지 않으면 다른 환경에서 사례별로 문제가 발생합니다.

일부 환경은 msgpack / protobuf와의 직렬화 및 역 직렬화가 매우 빠르며 다른 환경은 그다지 많지 않습니다. 일반적으로 언어 / 환경 수준이 낮을수록 이진 직렬화가 더 잘 작동합니다. 고급 언어 (node.js, .Net, JVM)에서는 JSON 직렬화가 실제로 더 빠르다는 것을 종종 알 수 있습니다. 문제는 네트워크 오버 헤드가 메모리 / CPU보다 다소 제한되어 있습니까?

msgpack vs bson vs protocol buffers와 관련하여 msgpack은 그룹의 최소 바이트이며 프로토콜 버퍼는 거의 같습니다. BSON은 다른 두 가지보다 더 광범위한 기본 유형을 정의하며 객체 모드와 더 잘 일치 할 수 있지만 더 장황하게 만듭니다. 프로토콜 버퍼는 스트리밍하도록 설계되어 이진 전송 / 저장 형식에보다 자연스러운 형식으로 만들어집니다.

개인적으로, 트래픽이 적을 필요가없는 한 JSON이 직접 제공하는 투명성에 의존합니다. gzip으로 압축 된 데이터가있는 HTTP보다 네트워크 오버 헤드의 차이는 형식 간 문제가 훨씬 적습니다.


6
Native MsgPack은 " 항상 존재하는 텍스트 "인 키의 길이가 "a"또는 "b"와 같이 거나 전체 페이로드에서 중요하지 않은 부분이므로 크기별로 ProtocolBuffers 만 사용하면 효율적입니다 . IDL / 컴파일을 사용하여 필드 디스크립터를 ID에 맵핑하는 ProtocolBuffers는 항상 짧습니다. 이것은 또한 MsgPack을 "동적"으로 만드는 이유이며, 이는 ProtocolBuffers가 가장 확실하지 않습니다.
user2864740

2
종점은 좋지만 : gzip / deflate는 실제로 키가 더 길지만 반복적으로 많은 경우 (MsgPack, JSON / BSON 및 XML 등 많은 레코드에서) 중복 키를 처리 하지만 실제로는 도움이되지 않습니다. 프로토콜 버퍼는 여기에 있습니다. Avro는 스키마를 개별적으로 전송하여 키 중복성을 수동으로 제거합니다.
user2864740

4

빠른 테스트는 축소 된 JSON이 이진 MessagePack보다 빠르게 역 직렬화됨을 보여줍니다. 테스트에서 Article.json은 550kb 축소 JSON이고 Article.mpack은 420kb MP 버전입니다. 물론 구현 문제 일 수 있습니다.

메시지 팩 :

//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)의 압축을 풀고 테스트 한 결과 10,000 회 반복 simplejson2.6.2는 66.7 초가 msgpack걸리고 0.2.2는 28.8이 소요됩니다.

2
이 Article.json은 어디에서 왔습니까?
Ant6n

여러분, 테스트 코드는 위의 의견에 있습니다. 기타 다른 예상은 Article.json이 프로젝트에서 JSON 직렬화 된 객체입니다. 그리고 지금까지 그 결과는 어쨌든 관련이 없을 수도 있습니다
Oleksiy Khilkevich

14
JS는 기본적으로 C ++로 JSON을 구현하고 JS는 msgpack을 사용하므로 성능 비교가 적절하지 않습니다.
Alex Panchenko

2
MessagePack이 로마보다 라틴어를 더 잘 사용하도록하려고합니다. JSON은 JavaScript에 기본 (C ++) 인 반면 MessagePack은 JavaScript로 작성되며 해석됩니다. 이것은 기본적으로 두 개의 코드 스 니펫을 비교하는 것입니다. 하나는 JavaScript로 작성되고 다른 하나는 C ++로 작성됩니다.
Ramazan Polat

0

아직 언급되지 않은 주요 차이점은 BSON에 전체 문서 및 추가로 중첩 된 하위 문서에 대한 크기 정보가 바이트 단위로 포함되어 있다는 것입니다.

document    ::=     int32 e_list

이는 크기와 성능이 중요한 제한된 환경 (예 : 임베디드)에 두 가지 주요 이점이 있습니다.

  1. 구문 분석 할 데이터가 완전한 문서를 나타내는 지 또는 어느 시점에서 (연결 또는 저장 장치에서든) 더 요청해야하는지 즉시 확인할 수 있습니다. 이것은 비동기 작업 일 가능성이 높으므로 구문 분석 전에 이미 새 요청을 보낼 수 있습니다.
  2. 데이터에 관련없는 정보가 포함 된 전체 하위 문서가 포함되어있을 수 있습니다. BSON을 사용하면 하위 문서의 크기 정보를 사용하여 하위 문서의 크기 정보를 사용하여 하위 문서를지나 다음 객체로 쉽게 이동할 수 있습니다. 반면 msgpack에는 맵 (BSON의 하위 문서와 유사) 내에있는 요소의 수가 포함되어 있습니다. 이것은 의심 할 여지없이 유용한 정보이지만 구문 분석기에는 도움이되지 않습니다. 여전히 맵 내의 모든 단일 오브젝트를 구문 분석해야하며 건너 뛸 수 없습니다. 데이터 구조에 따라 성능에 큰 영향을 줄 수 있습니다.

0

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

0

글쓴이가 말했듯이 MessagePack은 원래 네트워크 통신용으로 설계되었으며 BSON은 스토리지 용으로 설계되었습니다.

MessagePack은 컴팩트하지만 BSON은 자세합니다. MessagePack은 공간 효율적이며 BSON은 CURD (시간 효율적)를 위해 설계되었습니다.

가장 중요한 것은 MessagePack의 유형 시스템 (접두사)이 허프만 인코딩을 따르는 것입니다. 여기서 허프만 트리를 MessagePack으로 그렸습니다 (이미지를 보려면 링크 클릭).

허프만 MessagePack의 나무

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.