数据存储和传输的二进制编码格式概述
原创 夏群林 2025.12.27
数据存储与传输,一是追求“高效”,即体积足够小,降低硬盘存储成本、减少网络带宽消耗;同时,解析足够快,减少CPU运算开销。二是必须“兼容”,即能跨编程语言、跨硬件设备正常交互,最好能贴合业务场景的特殊需求,如数据库的索引,嵌入式设备的低内存占用刚需。
JSON(JavaScript Object Notation)大体满足这些需求,成为主流文本格式,深刻影响跨系统数据交互。所有现代浏览器均原生支持JSON序列化/反序列化,几乎所有编程语言都内置相关工具,无需额外依赖,是前端与后端、跨语言系统数据交互的通用语言。“轻量级文本数据交换格式”的核心定位,极其成功。
JSON诞生于2002年,基于JavaScript的对象字面量语法,相对于其后各种各样的数据编码格式,其核心价值,在于奠定了数据格式的易用性基准:
- 人类可读可写:采用纯文本结构,用{}、[]、:、,等简单语法标记对象、数组与键值对,无需工具即可直接阅读和修改,调试成本极低,这是其区别于二进制格式的核心特征;
- 可表达任意数据类型:虽原生仅支持字符串、数字、布尔、数组、对象、null六种基础类型,但通过对象、数组的嵌套组合,理论上可表达任意复杂数据结构,适配多数业务场景的存储传输需求。
例如:描述用户信息的嵌套JSON结构- {
- "id":1001,
- "name":"张三",
- "age":28,
- "isVIP":true,
- "address":
- {
- "province":"广东",
- "city":"深圳"
- },
- "hobbies":["读书","运动"]
- };
复制代码 是不是一目了然?当然,有代价:
- 冗余:为保证可读性,JSON需用大量冗余字符,如双引号包裹字符串、冒号分隔键值、逗号分隔元素。一个键值对{"age":25},JSON需10个字符(UTF-8编码下占10字节),有效数据(“age”和25)仅占4字节,冗余率超50%;上述用户信息JSON(共118个字符,占118字节),冗余字符占比达35%以上;
- 解析慢:JSON本质是文本,解析时需先逐字符扫描语法,识别{}、:等符号,再将字符串类型的数值、布尔值转换为程序原生类型,如“25”转int,步骤繁琐且耗时,CPU开销大;
- 原生类型少:仅支持字符串、数字(无整数/浮点数细分)、布尔、数组、对象、null六种类型,缺少数据库/复杂应用必需的类型,如时间戳、二进制数据、正则表达式、地理空间数据等;
在JSON之前,XML与YAML曾是文本类数据交换的核心选择,XML以强结构化、可扩展性著称,适合复杂文档与配置场景;YAML以简洁的缩进语法实现可读性与结构化平衡。JSON则平衡了结构化能力与轻量简洁,解析效率优于XML、兼容性强于YAML,成为跨系统文本交互的首选。
随着应用场景从轻量交互转向海量数据存储与高频通讯,冗余成本会急剧放大。在微服务高频RPC调用、物联网设备低带宽传输、海量日志存储等场景中,体积与解析速度成为性能瓶颈。JSON的固有痛点逐渐凸显,二进制编码格式随之诞生。Protobuf、MessagePack、BSON、CBOR等,让人眼花缭乱。
二进制编码的基本原理
所有二进制编码格式,无论定位如何差异,其底层都共享一套核心转换原理:类型/长度标记 + 原生二进制数据,这是理解各类二进制格式设计逻辑的基础。它以解决JSON在存储传输中体积大、解析慢痛点为目标,剔除冗余、直接存储有效数据,替代JSON的文本语法+字符串存储模式,适配存储传输对小体积、快解析的需求。
- 类型/长度标记(Tag):核心是用1~4字节的二进制小标签,同时说明两个关键信息,数据类型(如整数、字符串、对象)和数据长度(如字符串占4字节、数组含2个元素),直接替代JSON的冗余语法。
比如:表达“name字段,值为张三(字符串类型,长度4)”,JSON需写“"name":"张三"”(含双引号、冒号共8字节);二进制格式用1字节标记(比如0xA4,其中高4位表字符串类型,低4位表长度4)+4字节“张三”的UTF-8编码,总字节数仅5字节,直接省掉3字节冗余;
- 原生二进制数据(Value):按计算机“原生语言”存储数据,避免JSON“所有数据转字符串”的低效操作。
数据内容JSON存储(字符串形式)二进制存储(原生格式)体积节省比例整数25“25”(2字节)0x19(1字节varint编码)50%布尔值true“true”(4字节)0x01(1字节)75%字符串“张三”“"张三"”(6字节)0xE5BCA0E4B889(4字节UTF-8)33%
- 结构化组织:整个二进制流由“标记+数据”的字段单元串联而成,无额外分隔符。解析逻辑简单直接:先读标记(知道后续数据是“字符串+长度4”),再精准读4字节数据即可,无需像JSON那样逐字符扫描“{、}、:”等语法符号。
比如解析上述用户信息,JSON需扫描118个字符识别语法,二进制格式仅需按“读标记→读数据”的逻辑读取6个字段单元,解析速度提升至少2倍;
基于这套核心转换原理,各类二进制格式的差异本质是对性能、灵活性、通用性三大维度的权衡:
- 性能:核心衡量指标为“体积压缩比”(相较于文本格式的字节数减少比例)和“解析速度”(序列化/反序列化的CPU耗时)。极致性能通常依赖“精简冗余信息”“原生数据存储”“预定义结构”等设计。
- 灵活性:核心是“数据结构的适配能力”,包括是否需要预定义Schema(协议)、支持的数据类型丰富度、能否动态新增/删除字段。高灵活性适合异构数据、频繁迭代的场景。
- 通用性:核心是“跨场景、跨系统的适配能力”,包括跨语言支持广度、官方/社区标准化程度、生态工具完善度(如调试工具、集成插件)。高通用性降低多系统协作的沟通与开发成本。
这三大维度决定了它们的优化方向,有侧重性能的,有侧重灵活性的。Protobuf、MessagePack、BSON是当前应用最广泛的三类二进制编码格式,分别代表了通讯优先、通用兼容优先、文档存储优先的设计方向,三者共同占据二进制编码市场70%以上份额。它们均遵循类型/长度标记+原生二进制数据的核心转换原理,只是对三大维度的权衡不同,形成了差异化的适用场景。
MessagePack:JSON的二进制平替
MessagePack的核心定位是“像JSON一样易用,但更小、更快”,本质是JSON的二进制超集。由社区主导开发,无单一官方强制主导方,其标准化由社区主导,拥有清晰的编码规范,虽无官方强制标准,但社区的实现高度统一。零学习成本、无缝兼容JSON,是最通用的二进制格式。
MessagePack 严格遵循“类型/长度标记+原生二进制数据”的核心转换原理,重点保留JSON的灵活性,不做极致压缩以追求性能:
- 类型/长度标记:灵活性优先:用1字节二进制标记同时表类型与长度(如0xA4表“长度4的字符串”,0x19表“正整数25”),替代JSON的双引号、冒号等冗余字符。为兼容动态类型,标记覆盖更多场景,体积略大于Protobuf,但灵活性更高;
- 原生数据+类型扩展:按原生格式存储数据(整数存varint、字符串存UTF-8),同时完整支持JSON所有基础类型,新增二进制数据、64位整数等JSON缺失类型;
- 无Schema设计:无需预定义结构,可直接序列化/反序列化任意JSON兼容数据,解析逻辑与JSON一致但更高效,开发者无需改变习惯即可升级。
MessagePack 的综合表现
- 性能:中高——体积比JSON小30%-50%,解析速度是JSON的2-10倍,略逊于Protobuf但远超BSON;
- 灵活性:极高——无Schema约束,支持动态新增字段、异构数据,可与JSON双向无缝转换,调试时可直接转为JSON查看;
- 通用性:极强——支持50+编程语言(覆盖主流及小众语言),社区库成熟,集成成本低,是“不想改架构却想优化JSON性能”的最优解。
MessagePack 典型应用
Redis缓存的二进制存储格式、Elasticsearch的部分数据序列化、前后端高性能API交互(如移动端节省流量)、多语言微服务的轻量数据交换、日志系统的压缩存储。
Protobuf:通讯高性能之选
Protobuf(Protocol Buffers)由Google于2008年开源,核心定位是跨系统高效通讯,专为解决微服务、分布式系统间数据传输的“体积大、解析慢、版本兼容难”问题设计。其标准化由Google主导,目前最新稳定版为Protobuf 3,拥有严格的语法规范(.proto文件)和多语言官方实现指南,是工业级通讯场景的事实标准。
Protobuf 本质是对类型/长度标记+原生二进制数据原理的极致优化版落地,通过静态Schema、字段编号等设计,进一步压缩标记体积、提升解析效率。
<ul>静态Schema奠基:需提前通过.proto文件定义数据结构(含字段名、类型、唯一编号),编译后生成对应语言代码。强类型约束使“类型/长度标记”更精简(无需兼容动态类型),同时减少解析错误,为高效编码奠定基础。
字段标记极致压缩:遵循“标记+数据”逻辑,将标记设计为“字段编号+类型编号”的复合结构,核心计算公式为 tag = (字段编号 |