序列化协议怎么选?JSON、Protobuf、Avro 对比分析

更新日期: 2026-04-24 阅读: 12 标签: 序列化

一、什么是序列化

序列化就是把内存里的数据对象转成字节流或者字符串,方便存到硬盘或者通过网络发出去。反序列化就是反过来,把字节流再转回内存里的对象。

内存对象 → 序列化 → 字节流 → 网络传输或存盘
内存对象 ← 反序列化 ← 字节流

序列化协议为什么重要?有三个原因:

  • 性能影响:在高并发场景下,序列化和反序列化能占到CPU时间的30%到50%

  • 带宽成本:序列化后的数据越大,花在网络传输上的钱就越多

  • 开发效率:协议好不好用,直接影响开发改东西的速度


二、主流序列化协议对比

协议类型可读性体积速度支持语言
JSON文本很好几乎所有语言
XML文本较好最大最慢几乎所有语言
Protobuf二进制主流语言
MessagePack二进制中等较快几乎所有语言
Thrift二进制主流语言
Avro二进制最小主要是JVM生态

三、JSON:简单但成本高

JSON的设计目标就是简单、人类可读、通用。下面是一个例子:

{
  "userId": 10001,
  "userName": "张三",
  "age": 28,
  "hobbies": ["编程", "阅读"],
  "createTime": "2024-01-15T10:30:00Z"
}

JSON有三个主要性能问题。

第一个问题:文本编码太占空间

JSON用UTF-8文本编码,每个数字字符占1个字节。比如数字12345,JSON里要存5个字节('1','2','3','4','5')。如果换成二进制存,int32只要4个字节,int64只要8个字节。数字越大,浪费越多。

第二个问题:解析过程复杂

JSON解析要经过词法分析、语法分析、建抽象语法树、类型推断转换这些步骤。每个字符都要做边界检查、ASCII转换、累乘运算、溢出检测,开销不小。

第三个问题:没有Schema,得用反射

JSON没有预先定义的数据结构,反序列化的时候得用反射来判断类型。反射本身就有性能开销,还要做字段名字符串匹配和类型检查。

JSON什么时候用

适合做公开API、第三方对接、配置文件、日志输出、前后端通信。不适合做高性能内部服务调用、大数据量传输、实时性要求高的场景。


四、Protobuf:Google的工业标准

Protobuf是Google开源的序列化框架,性能好主要靠三个设计。

第一个设计:二进制加Varints变长整数

Varints编码用每个字节的最高位表示有没有后续字节。1表示还有下一个,0表示这是最后一个。

小数字在Protobuf里只占1个字节,JSON要占1到3个字节。数值越大,Protobuf省得越多。

第二个设计:Schema预编译

Protobuf要先写.proto文件定义数据结构:

syntax = "proto3";

message User {
  int32 user_id = 1;
  string user_name = 2;
  int32 age = 3;
  repeated string hobbies = 4;
  int64 create_time = 5;
}

预编译有两个好处:字段名不传输,只传字段编号;生成强类型代码,不用反射,直接写内存。

第三个设计:TLV编码

每个字段按Tag-Length-Value的格式存。Tag里包含了字段编号和类型信息。

Protobuf什么时候用

适合微服务内部通信、gRPC、高频RPC调用、游戏实时数据同步、移动端API。不适合公开API、需要人眼可读的场景、前端直接用的数据。


五、其他序列化协议

MessagePack

可以理解成JSON的二进制版,保持了JSON的灵活性,但通过二进制编码把体积压缩了。比如一个27字节的JSON,转成MessagePack大约18字节。

适合需要JSON灵活性但想要更好性能的场景,也适合Redis存储优化。

Apache Thrift

Facebook开发,支持多种序列化协议。二进制协议适合生产环境,压缩协议适合带宽紧张的场景,JSON协议方便调试。

适合大数据生态里的Hadoop、HBase,也适合跨语言RPC服务。

Apache Avro

核心设计是Schema和数据分离。序列化后的数据只存值,不存字段信息,所以体积最小。Schema单独存放,改字段规则时新老数据都能兼容。

适合大数据存储里的Hadoop、Hive,还有数据湖、流式数据处理。


六、选型决策基础

选序列化协议主要看三点:数据要不要给人看、性能要求高不高、跨语言场景多不多。

需要人看或者对外提供接口,选JSON或YAML。追求极致性能且内部服务用,选Protobuf。做大数据存储且Schema经常变,选Avro。既要灵活性又想比JSON强一点,试试MessagePack。


七、不同场景怎么选

微服务内部通信:Protobuf

配合gRPC用,自动生成客户端代码,类型安全,性能高。

对外公开API:JSON

开发者不用额外装工具就能用,调试方便,生态丰富。

大数据存储:Avro加Parquet

Avro做序列化,Parquet做列存,压缩好,查询快。

移动端网络通信:Protobuf

体积小省流量,解析快省电。可以再用sint32编负数、packed编数组这些技巧进一步优化。


八、总结

下面是快速选型参考:

场景推荐协议原因
微服务内部Protobuf高性能、类型安全、有代码生成
公开APIJSON通用、生态好、方便调试
配置文件YAML可读性好、支持注释
大数据存储AvroSchema能演进、对列存友好
游戏实时通信Protobuf延迟低、体积小
移动端APIProtobuf省流量、解析快

记住几条原则:不要一味追求性能,JSON在大部分场景下已经够用了。要考虑团队的学习成本,Protobuf需要多学一套工具链。可以混着用,对外给JSON,内部跑Protobuf。最后一定要在自己真实场景里跑一跑压测,别人的数据只能做参考。

本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/13695

相关推荐

Jquery如何序列化form表单数据为JSON对象

jquery中serialize()方法做的是将表单中的数据以htpp请求格式拼接成字符串。serialize确实是能够解决一般的提交数据,但是有时我们需要的是一个object对象,而不是字符串(比如jqgrid reload时设置查询条件参数,就需要object对象)。

如何使用 JSON.stringify() 去序列化一个 Error?

最近在做 Node 服务端需求的时候,遇到了几次服务端报错的问题。打 log 发现均是一些 Error,但是它们都没法很好地透传给前端浏览器,出现问题只能查看服务端机器的日志,调试起来非常不方便。思考了一下

JS对象序列化、对象的toString()与对象的valueOf()

当对象的value为undefined和Object时会被忽略,当toString和valueOf都存在时,在进行操作时,都会尝试转换成基本类型,先找valueOf,如果返回基本类型,这只调用valueOf,如果不是,比如是对象的话,就去找toString,如果也返回Object,就会报错

如何将JSON反序列化为JavaScript对象?

JSON(JavaScript Object Notation)用于与Web服务器或RESTFull API交换数据,从Web服务器接收的数据始终是字符串。为了使用这些数据,您需要使用JSON.parse()解析数据,它将返回一个JavaScript对象或对象数组。

JavaScript 链式结构序列化详解

其实看看上面的代码,不难发现,if…else这种格式,其实就是数据结构中的单链表,那么,初步利用JavaScript实现单链表,如下:

JavaScript 中URL 查询字符串(query string)的序列与反序列化

在 JavaScript 中,可以使用 URLSearchParams 对象来处理 URL 中的查询字符串。序列化(将 JavaScript 对象转换为查询字符串)可以使用 URLSearchParams 对象的 append() 方法

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!