Protobuf 序列化原理学习
1. 基本概念
-
消息
:由于Protocp Buffer主要用于数据存储、网络通信的场景,将结构化的数据(数据结构或对象)进行序列化,生成二进制串来保存或传输。把要序列化的结构化数据称为**消息
**。 -
T - L - V
的数据存储方式:即Tag - Length - Value
,标识 - 长度 - 字段值 存储方式。以 标识 - 长度 - 字段值 表示单个数据,最终将所有数据拼接成一个字节流。 -
T - V
的数据存储方式:即Tag - Value
,标识 - 字段值 存储方式。Varint
和Zigzag
编码后的格式就是这种,不需要记录长度,每个字节的最高位去记录下一个字节是否属于当前数字。
2. 三个结论
-
结论1:
Protocol Buffer
将消息中的每个字段进行编码,然后利用T - L - V
存储方式对数据进行存储,最终得到一个二进制字节流。 -
结论2:
Protocol Buffer
对于不同数据类型采用不同的序列化方式(编码方式 & 数据存储方式)
-
对于存储
Varint
编码数据,就不需要存储字节长度Length
,所以实际上Protocol Buffer
的存储方式是T - V
; -
若
Protocol Buffer
采用其他编码方式(如LENGTH_DELIMITED
)则采用T - L - V
-
结论3:因为
Protocol Buffer
对于数据字段值的 独特编码方式 &T - L - V
数据存储方式,使得Protocol Buffer
序列化后数据量体积如此小
3. 编码方式
3.1 Varint
编码
- 定义:一种变长的编码方式
- 原理:用字节表示数字:值越小的数字,使用越少的字节数表示
- 作用:通过减少表示数字的字节数从而进行数据压缩
如:
- 对于 int32 类型的数字,一般需要 4个字节 表示;
- 若采用 Varint编码,对于很小的 int32 类型 数字,则可以用 1个字节 来表示
- 虽然大的数字会需要 5 个 字节 来表示,但大多数情况下,消息都不会有很大的数字,所以采用 Varint方法总是可以用更少的字节数来表示数字
Varint
编码的不足:
在计算机内,负数一般会被表示为很大的整数。(因为计算机定义负数的符号位为数字的最高位)
因此,使用**Varint
** 编码负数一定需要5个bytes。
解决方案: Protocol Buffer
定义了 sint32 / sint64
类型表示负数,先使用 Zigzag
编码(将 有符号数 转换成 无符号数),再采用 Varint
编码,从而用于减少编码后的字节数。
3.2 Zigzag
编码
-
定义:一种变长的编码方式
-
原理:使用 无符号数 来表示 有符号数字;
-
作用:使得绝对值小的数字都可以采用较少字节来表示;
-
Zigzag
编码 是补充Varint
编码在 表示负数 的不足,从而更好的帮助Protocol Buffer
进行数据的压缩 -
所以,如果提前预知字段值是可能取负数的时候,记得采用
sint32 / sint64
数据类型
- 对于
int32 / int64
类型的字段值(正数),Protocol Buffer
直接采用Varint
编码- 对于
sint32 / sint64
类型的字段值(负数),Protocol Buffer
会先采用Zigzag
编码,再采用Varint
编码
参考文献
摘抄搬运自:
Protocol Buffer 序列化原理大揭秘 - 为什么Protocol Buffer性能这么好?_flowbuf 的序列化价值-CSDN博客