JSON Toolkit

JSON nhị phân: chọn giữa MessagePack, CBOR và BSON

Kết luận: MessagePack, CBOR và BSON đều nén JSON thành byte. Hãy so sánh chi phí byte, cách xử lý int64 và dữ liệu nhị phân, rồi có một ranh giới rõ ràng cho việc khi nào chuyển đổi là đáng.

Trung cấpĐọc khoảng 10 phútXuất bản 2026-10-31Cập nhật 2026-10-31
Ảnh bìa so sánh các định dạng JSON nhị phân MessagePack, CBOR và BSON

Kết luận: giữ JSON văn bản cho trao đổi bên ngoài và cho dữ liệu con người cần kiểm tra, và chỉ chuyển sang MessagePack hoặc CBOR trên các liên kết nội bộ khi payload nhị phân, số nguyên vượt 2^53, hoặc số thập phân chính xác khiến kích thước nén hay khả năng biểu diễn kiểu trở thành vấn đề thật; BSON thuộc về hệ sinh thái MongoDB.

JSON văn bản tiết kiệm khoảng trắng; định dạng nhị phân tiết kiệm dấu câu và dạng văn bản của số. Tên trường vẫn xuất hiện một lần mỗi bản ghi, nên hãy tính mức tiết kiệm thật trước khi chuyển đổi.

1. JSON nhị phân thật sự tiết kiệm được gì

Hãy đặt kỳ vọng trước. Không định dạng nào trong số này là "một cú pháp JSON khác". Chúng mã hóa cùng một mô hình dữ liệu thành byte: object, array, string, number, boolean và null. Những gì chúng loại bỏ rất cụ thể:

  • Các ký tự cấu trúc như ngoặc kép, dấu hai chấm, dấu phẩy và ngoặc nhọn;
  • Dạng văn bản thập phân của số, nên 32 trở thành một byte duy nhất;
  • Việc quét lại để tìm ranh giới chuỗi, được thay bằng tiền tố độ dài cố định;
  • Mã hóa Base64 cho payload nhị phân, vốn có thể lưu trực tiếp dưới dạng byte.

Những gì chúng không thể loại bỏ cũng rất cụ thể: tên trường vẫn xuất hiện dưới dạng chuỗi trong mọi bản ghi. Một map MessagePack, một map CBOR và một tài liệu BSON đều lặp lại khóa cho từng mục. Vậy nên khi JSON của bạn là mười nghìn bản ghi có cùng hình dạng, khoảng cách kích thước nén giữa văn bản và nhị phân nhỏ hơn nhiều so với trực giác, vì gzip giỏi nhất ở việc loại bỏ tên trường lặp lại.

2. Ba định dạng đến từ đâu

Định dạng Đặc tả và năm Nguồn gốc Cách dùng điển hình
MessagePack Đặc tả msgpack.org, từ 2008 Thiết kế độc lập tập trung vào tối giản Giá trị bộ nhớ đệm, IPC, pipeline nhật ký
CBOR RFC 8949 (STD 94), 2020 Chịu ảnh hưởng của MessagePack, được IETF chuẩn hóa COSE, WebAuthn, CWT, thiết bị hạn chế
BSON bsonspec.org, hệ sinh thái MongoDB Thiết kế cho tài liệu cơ sở dữ liệu Lưu trữ và giao thức truyền tải của MongoDB

CBOR được chuẩn hóa nhất: kiểu phương tiện application/cbor đã đăng ký IANA, quy tắc mã hóa tất định, và CDDL để mô tả. MessagePack nhẹ nhất, với đặc tả vừa một trang. BSON nặng nhất: nó đưa ObjectId, Decimal128, ngày tháng và các kiểu con nhị phân vào hệ kiểu, đó là lý do nó chỉ thật sự phổ biến trong hệ sinh thái MongoDB.

3. Cùng một dữ liệu, bốn cách mã hóa

Lấy {"name":"joe"} làm ví dụ. Chi phí byte rất dễ so sánh:

Cách mã hóa Byte Chuỗi byte
JSON văn bản 14 7b 22 6e 61 6d 65 22 3a 22 6a 6f 65 22 7d
MessagePack 9 81 a3 6e 61 6d 65 a3 6a 6f 65
CBOR 10 a1 64 6e 61 6d 65 63 6a 6f 65
BSON 19 4 byte tổng độ dài + byte kiểu + name\0 + 4 byte độ dài giá trị + joe\0 + byte kết thúc

Tách chín byte của MessagePack ra:

  • 81: nửa cao 1000 nghĩa là map, nửa thấp 0001 nghĩa là một cặp khóa-giá trị;
  • a3: ba bit cao 101 nghĩa là chuỗi độ dài cố định dài 3;
  • 6e 61 6d 65: ASCII của name;
  • a3: một chuỗi khác dài 3;
  • 6a 6f 65: joe.

a1 của CBOR là kiểu chính 5 (map) với độ dài 1, và 64 là kiểu chính 3 (chuỗi văn bản) với độ dài 4, nên ngữ nghĩa gần như đẳng cấu với MessagePack. BSON lớn hơn: mỗi tài liệu mang 4 byte tổng độ dài, mỗi trường lặp lại một byte kiểu và tên, và mỗi chuỗi lặp lại độ dài cộng \0 ở cuối. Với tài liệu nhỏ, BSON có thể dễ dàng lớn hơn JSON văn bản.

Bạn có thể kiểm tra trực tiếp các tiền tố bằng cách hiển thị các byte đó dưới dạng hex:

const messagePackBytes = [0x81, 0xa3, 0x6e, 0x61, 0x6d, 0x65, 0xa3, 0x6a, 0x6f, 0x65];
const hexText = messagePackBytes.map((byte) => byte.toString(16)).join(' ');
console.log(hexText); // 81 a3 6e 61 6d 65 a3 6a 6f 65

3.1 Kích thước và tốc độ phân tích: bỏ qua các con số quảng cáo

Những tuyên bố rằng nhị phân "nhanh gấp 10 lần" nên được tách thành hai câu hỏi.

Kích thước. Khi không nén, nhị phân nhỏ hơn vì dấu câu và khoảng trắng đã biến mất. Nhưng pipeline thực tế hầu như luôn nén: sau gzip hoặc brotli, tên trường lặp lại bị nén mất, và lợi thế nhị phân thu hẹp đáng kể. Hãy làm theo phương pháp trong Nén JSON: minify so với gzip: đo byte đã nén trước, rồi quyết định việc đổi định dạng có đáng hay không.

Tốc độ phân tích. Nhị phân bỏ qua bước phân tích từ vựng (quét ngoặc kép, dấu phẩy và ký tự số), nên về lý thuyết nó phải nhanh hơn. Tuy nhiên trong JavaScript, chi phí chi phối là cấp phát đối tượng và bộ nhớ, điều mà không bộ giải mã nào tránh được. Hình dạng dữ liệu, số trường và chất lượng hiện thực đều có ảnh hưởng, nên kết luận đáng tin duy nhất đến từ việc đo trên payload của chính bạn thay vì trích dẫn hệ số của nhà cung cấp. Nếu một payload quá lớn để đọc vào bộ nhớ cùng lúc, hãy chia lát theo cách mô tả trong phân tích và truyền luồng JSON lớn trước khi đo lợi ích mã hóa.

4. Trường int64 và nhị phân: lý do thật để chuyển đổi

Kích thước thường không phải lý do đổi định dạng. Khả năng biểu diễn kiểu mới là.

4.1 Số nguyên vượt 2^53

Số JSON rơi vào double trong JavaScript, nên 9007199254740993 biến thành 9007199254740992. Ba định dạng nhị phân xử lý việc này khác nhau:

Định dạng Số nguyên 64-bit 64-bit không dấu Độ chính xác tùy ý
MessagePack ✓ int64 (0xd3), giải mã được thành BigInt ✓ uint64 (0xcf) ✗ (cần một kiểu mở rộng)
CBOR ✓ các dạng 64-bit của kiểu chính 0/1 ✓ Cùng không gian mã hóa ✓ tag bignum 2/3
BSON ✓ int64 (0x12) ✗ Chỉ có dấu ✗ (Decimal128 là số điểm cố định, không phải số nguyên tùy ý)

CBOR là định dạng duy nhất có biểu diễn chuẩn cho số nguyên độ chính xác tùy ý: các giá trị vượt 64 bit được bọc trong tag 2 (không âm) hoặc tag 3 (âm) quanh một chuỗi byte. MessagePack và BSON phải quay về chuỗi hoặc quy ước mở rộng tự chế.

4.2 Payload nhị phân

JSON chỉ có đúng một cách biểu diễn dữ liệu nhị phân: Base64, vốn làm payload phình lên khoảng một phần ba và cần thêm một bản sao khi giải mã. MessagePack có kiểu bin (0xc4–0xc6), CBOR có chuỗi byte (kiểu chính 2), và BSON có các kiểu con nhị phân. Khi bạn truyền ảnh, hash, vector hoặc khối đã nén, khác biệt này quan trọng hơn nhiều so với việc tiết kiệm vài byte dấu câu.

5. So sánh song song

Khía cạnh MessagePack CBOR BSON
Đặc tả Đặc tả cộng đồng IETF RFC 8949 Hệ sinh thái MongoDB
Kiểu phương tiện application/msgpack (không phải chuẩn IANA) application/cbor (đã đăng ký IANA) Không có kiểu phương tiện phổ quát
Hệ kiểu Sáu kiểu JSON cộng bin và ext Sáu kiểu JSON cộng bin, tag và giá trị đơn giản Rộng hơn: ObjectId, Decimal128, ngày tháng, regex, timestamp
Cơ chế mở rộng Kiểu mở rộng (ví dụ timestamp -1) Tag (ngày tháng, bignum, CBOR nhúng) Mã kiểu cố định, mở rộng theo quy ước hệ sinh thái
Mã hóa tất định Không có yêu cầu chuẩn Quy tắc rõ ràng, dùng được cho chữ ký Không có
Tự mô tả Có, nhưng không có lược đồ cấp trường Có, và mô tả được bằng CDDL Có, với tên trường lặp lại theo từng tài liệu
Chi phí tài liệu nhỏ Thấp Thấp Cao (tiền tố độ dài, tên trường, byte kết thúc)
Hệ sinh thái Bộ nhớ đệm, nhật ký, một số RPC COSE, WebAuthn, IoT Chỉ MongoDB

6. Hỗ trợ trên trình duyệt và Node

Node 24 không kèm bộ mã hóa nào trong số này, nên bạn phải cài thư viện. Các lựa chọn phổ biến:

Môi trường chạy MessagePack CBOR BSON
Node.js @msgpack/msgpack, msgpackr cbor-x, borc bson
Trình duyệt @msgpack/msgpack bản trình duyệt của cbor-x, cbor-web bson

Cả hai họ đều làm việc trên Uint8Array và không cần Buffer. Hãy chú ý khác biệt giữa các phiên bản:

import { encode, decode } from '@msgpack/msgpack';

/**
 * Encodes an object to MessagePack bytes and decodes it immediately for verification.
 * @param payload Plain object to encode
 * @returns The decoded value and the encoded byte length
 */
function roundTrip(payload: Record<string, unknown>): { decoded: unknown; byteLength: number } {
  const bytes = encode(payload);
  return { decoded: decode(bytes), byteLength: bytes.byteLength };
}

Hầu hết thư viện giải mã số nguyên lớn thành number theo mặc định và mất độ chính xác vượt phạm vi an toàn. Nếu giá trị chính xác quan trọng, hãy bật tùy chọn BigInt một cách rõ ràng (useBigInt64 trong @msgpack/msgpack, một công tắc tương đương trong cbor-x), nếu không bạn chỉ đơn giản là chuyển vấn đề cũ của JSON sang một định dạng mới.

7. Khả năng gỡ lỗi và tương thích giao thức

7.1 Khả năng gỡ lỗi: chi phí ẩn lớn nhất

Ưu điểm lớn nhất của JSON văn bản không phải kích thước; mà là bạn có thể mở nó bất cứ lúc nào. Sau khi chuyển sang nhị phân:

  • grep, awk và jq đều không còn dùng được, và pipeline nhật ký cần thêm một bước giải mã;
  • Gỡ lỗi trên production cần đúng bộ giải mã ở đúng phiên bản, nếu không tên trường bạn thấy sẽ sai;
  • Lưu lượng bắt được hiện trong công cụ dành cho nhà phát triển của trình duyệt như một khối byte không đọc được;
  • Diff khi xem xét mã hiện ra một chuỗi thập lục phân.

CBOR có ký pháp chẩn đoán, hiển thị byte thành văn bản gần với JSON dễ đọc, ví dụ một map dưới dạng {1: 2}; MessagePack cũng có vài bộ giải mã dòng lệnh. Nhưng tất cả đều cần cài thêm và nằm ngoài đường thao tác chuẩn.

Kết luận: khi nào con người cần kiểm tra dữ liệu thường xuyên, chi phí ẩn của định dạng nhị phân sẽ vượt băng thông mà nó tiết kiệm.

7.2 Tương thích giao thức và mở rộng

  • CBOR có cơ chế tag đầy đủ nhất: tag 0 cho chuỗi ngày-giờ, tag 1 cho Unix timestamp, tag 2 và 3 cho bignum, và tag 24 cho CBOR nhúng. Kết hợp với mã hóa tất định, nó được dùng trong các giao thức cần chữ ký và tính nhất quán giữa các hiện thực, như COSE, WebAuthn và CWT.
  • MessagePack để kiểu mở rộng cho bên hiện thực. Thực tế kiểu phổ biến duy nhất là timestamp (kiểu mở rộng -1). Giữa các nhóm, mọi kiểu mở rộng phải được ghi vào tài liệu giao thức, nếu không bên kia không thể giải mã.
  • BSON có mã kiểu cố định, và ObjectId, Decimal128, MinKey, MaxKey là ngữ nghĩa riêng của MongoDB. Coi BSON như một định dạng trao đổi phổ quát sẽ mang theo một đống kiểu không dùng và tiền tố độ dài không dùng.

Nếu một giao thức yêu cầu "cùng dữ liệu phải sinh ra cùng byte", chỉ CBOR cung cấp câu trả lời chuẩn; MessagePack và BSON đều cần thỏa thuận thêm về thứ tự trường và chi tiết mã hóa.

8. Khi nào đáng chuyển đổi

Các tín hiệu biện minh cho việc chuyển đổi:

  • Payload lớn, cả hai đầu có thể thay đổi, và kích thước sau nén vẫn khác nhau đáng kể;
  • Bạn truyền nội dung nhị phân và mức phình do Base64 cùng chi phí giải mã đo được;
  • Bạn cần số nguyên 64-bit hoặc số thập phân chính xác mà double của JSON không biểu diễn được;
  • Liên kết là server-to-server và công cụ chẩn đoán có thể được chuẩn hóa.

Các tín hiệu phản đối:

  • gzip hoặc brotli đã được bật và lợi ích chỉ là vài phần trăm một chữ số;
  • API là công khai và bên thứ ba chỉ có client HTTP đa dụng;
  • Dữ liệu là nhật ký hoặc cấu hình mà con người đọc bằng tay;
  • Nhóm không có ngân sách để duy trì bộ giải mã và tương thích phiên bản.

Một con đường trung gian cũng hiệu quả: giữ JSON ở bên ngoài và dùng MessagePack cho bộ nhớ đệm nội bộ. Giá trị bộ nhớ đệm chỉ được dịch vụ của chính bạn đọc, nên cả kích thước lẫn chi phí phân tích đều tốt hơn, và việc xóa bộ nhớ đệm giải quyết mọi vấn đề mà không chạm vào hợp đồng.

9. Danh sách kiểm tra

  • [ ] So sánh số byte đã nén từ dữ liệu thật, không phải số byte chưa nén;
  • [ ] Quyết định chính sách số nguyên lớn: tùy chọn BigInt, truyền dạng chuỗi, hay bignum CBOR;
  • [ ] Gửi trường nhị phân dưới dạng bin hoặc chuỗi byte thay vì bọc trong Base64;
  • [ ] Ghi kiểu mở rộng và tag vào tài liệu giao thức và ghim phiên bản bộ giải mã;
  • [ ] Giữ một đường gỡ lỗi bằng JSON văn bản có thể bật trong sự cố production;
  • [ ] Chạy phép đo và ghi lại các con số tái lập được thay vì tuyên bố bằng lời.

Sự khác biệt giữa MessagePack và CBOR là gì?

Cách mã hóa của chúng rất giống nhau: map, mảng và chuỗi đều dùng tiền tố kiểu-cộng-độ dài gọn. Khác biệt chính là mức chuẩn hóa. CBOR là một IETF RFC với kiểu phương tiện IANA, cơ chế tag và quy tắc mã hóa tất định; MessagePack là đặc tả cộng đồng với kiểu mở rộng phụ thuộc thỏa thuận riêng. Chọn CBOR khi bạn cần chữ ký, tính nhất quán giữa các hiện thực, hoặc giao thức cho thiết bị hạn chế.

BSON có thể thay thế JSON như định dạng phổ quát không?

Không khuyến khích. BSON được thiết kế cho tài liệu cơ sở dữ liệu: mỗi tài liệu mang 4 byte tổng độ dài, mỗi trường lặp lại kiểu và tên, và tài liệu nhỏ thường lớn hơn JSON văn bản. Nó cũng gồm các kiểu riêng của MongoDB như ObjectId và MinKey. Ngoài MongoDB, phần lớn hệ kiểu của nó không được dùng trong khi hình phạt kích thước vẫn còn.

Định dạng nhị phân làm payload của tôi nhỏ đi bao nhiêu?

Tùy hình dạng dữ liệu. Khi tên trường lặp lại nhiều, JSON văn bản đã nén và định dạng nhị phân gần nhau; khi tên ngắn và dữ liệu đầy ký tự cấu trúc cùng số, lợi thế nhị phân rõ hơn. Hãy đo mẫu thật cả khi chưa nén và sau gzip, rồi quyết định, thay vì ước lượng bằng một hệ số chung.

Tôi vẫn gỡ lỗi dữ liệu nhị phân bằng jq được không?

Không. jq chỉ xử lý JSON văn bản và sẽ lỗi ngay với byte MessagePack hay CBOR. Bạn phải giải mã bằng thư viện tương ứng thành JSON trước rồi đưa cho jq; CBOR cũng có thể đi qua công cụ ký pháp chẩn đoán để thành văn bản đọc được. Giữ một đường gỡ lỗi văn bản rẻ hơn là cài công cụ khi cần.