参考官网, 序列化原理

底层二进制存储

message Test1 {

optional int32 a = 1;

}

并设置为a=150,序列化到一个文件中,查看文件,得到下面的二进制:

08 96 01

从底层存储的二进制值看出,Protobuf为什么这么快,节省内存了吧。

有以上的结果是因为 varints 这个特殊的东东。它可以让已个int数据类型的存储根据值的大小而自动改变存储的字节数。

varint 中的每个字节,除了最后一个字节,都有最重要的位集——这表示还会有更多的字节。每个字节的低7位用于存储以7位为一组的数字的两个补码表示形式,最先存储的是最低字节。

比如存储数字1,请看二进制格式:

0000 0001

因为只有一个字节,所以最高位是0.

比如存储数字300,请看二进制格式:

1010 1100 0000 0010

计算方法:

  • 1.先删除最高位。因为这位时没意义的,只是告诉我们是否叨叨数字的末尾。

    1010 1100 0000 0010

    → 010 1100 000 0010
  • 2.翻转字节。因为varint最先存储的是最低字节。

    010 1100 000 0010

    →000 0010 010 1100
  • 3.字节相加。还原最终的值。

    → 000 0010 ++ 010 1100

    → 100101100

    → 256 + 32 + 8 + 4 = 300

Protobuf 的快,小就是通过以上来实现的了。。。。。。

消息结构(Message Structure)

Protobuf 是一系列键值对。消息的二进制版本只使用字段的标签作为,每个字段的名称和声明类型只能在解码结束时通过引用消息类型的定义来确定。

当对消息进行编码时,键和值被连接到一个字节流中。当消息被解码时,解析器能够跳过它不认识的字段。通过这种方式,可以使旧代码(相对Protobuf消息定义的新旧)能够兼容新的字段而不用修改代码。为此,行格式消息中每对的“键”实际上是两个值——.proto文件中的字段号+一个线类型,通过该类型可以推断出数据长度。在大多数语言实现中,这个键被称为标记。

数据类型:

Type Meaning Used For
0 Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum
1 64-bit fixed64, sfixed64, double
2 Length-delimited string, bytes, embedded messages, packed repeated fields
3 Start group groups (deprecated)
4 End group groups (deprecated)
5 32-bit fixed32, sfixed32, float

流消息中的每个键都是一个varint,其值为(field_number << 3) | wire_type,也就是说,数字的最后三位存储了存储数据包的类型。

例如:

底层存储二进制是:

000 1000

那么原字段的类型就是根据(field_number << 3) | wire_type得到低三位得到 wire( 0 ),是一个 Varint 类型,也就是数字。剩下的几位右移,得到的是1,因此字段标签是1

所以字段原型应该是:

struct Message {

int32 | int64 | uint32 | uint64 | sint32 | sint64 | bool | enum xxx = 1;

};

再来看下两个字节的150:

  • 1.十六进制值为:

    96 01
  • 2.转换为二进制格式:

    1001 0110 0000 0001
  • 3.丢弃最后一位(没意义,只是判断是否是最后一个字节),并且翻转字节(varint是先存储最低字节),最后穿起来得到真正的二进制值,进而得到原值。
    • → 000 0001 ++ 001 0110 (drop the msb and reverse the groups of 7 bits)

      → 10010110

      → 128 + 16 + 4 + 2 = 150

由此看见,多字节的二进制存储,就是多了丢弃最后一位翻转字节的步骤。

更多数据类型

有符号整型(Signed Integers)

在Protobuf中,有符号的编码是利用了ZigZag编码,把有符号类型编码成一个比较大的无符号整型,提高了存储空间和提高序列化速度。

ZigZag编码是一种应用于大量使用小整型的场景的编码算法,可以提高编码速度。

非varint数字(Non-varint Numbers)

类型1,类型5的数字是按照小端序列来存储的。

Strings

类型为2(以长度分隔)意味着该值是varint编码的长度,后跟指定的数据字节数。

看这个例子:

message Test2 {
optional string b = 2;
}

在应用程序里设置 b 值为testing,序列化后得到下面的二进制串:

12 07 74 65 73 74 69 6e 67

加粗的字节是“testing”的UTF8。这里的键是0x12→字段号= 2,类型= 2。值中的varint长度是7,你看,我们在它后面找到了7个字节——我们的字符串。

嵌入类型(Embedded Messages)

message Test1 {
optional int32 a = 1;
}
message Test3 {
optional Test1 c = 3;
}

设置Test1的 a 为150,得到序列化十六进制值:

1a 03 08 96 01

最后三个字节与上面的第一个示例单独Test1并赋值150 (08 96 01)完全相同,它们的前面是数字3,嵌入式消息的处理方式与字符串完全相同(wire type = 2)。

Protobuf底层存储原理的更多相关文章

  1. HBase底层存储原理

    HBase底层存储原理——我靠,和cassandra本质上没有区别啊!都是kv 列存储,只是一个是p2p另一个是集中式而已! 首先HBase不同于一般的关系数据库, 它是一个适合于非结构化数据存储的数 ...

  2. python-变量&底层存储原理

    目录 1.变量 1.变量如何使用 2.变量存储的原理 --[ 重点 ] 3.变量存储要遵循印射关系 4.变量三要素 2.常量 3.底层优化 4.垃圾回收机制 1.变量 1.变量如何使用 1.什么是变量 ...

  3. HBase底层存储原理——我靠,和cassandra本质上没有区别啊!都是kv 列存储,只是一个是p2p另一个是集中式而已!

    理解HBase(一个开源的Google的BigTable实际应用)最大的困难是HBase的数据结构概念究竟是什么?首先HBase不同于一般的关系数据库,它是一个适合于非结构化数据存储的数据库.另一个不 ...

  4. Redis数据的底层存储原理

    redis底层是用什么结构来存储数据的呢? 我们从源码上去理解就会容易的多:   redis底层是使用C语言来编写的,我们可以看到它的数据结构声明.一个 dict 有两个dictht,一个dictht ...

  5. 列式数据库~clickhouse 底层存储原理

    简介:今天介绍列式数据库的一些基本原理 一  数据目录 Data目录 数据存储目录,数据按照part分成多个文件夹,每个文件夹下存储相应数据和对应的元信息文件 Metadata 表定义语句,存储所有表 ...

  6. V7000存储数据恢复_底层结构原理拆解及Mdisk磁盘掉线数据恢复方法

    Storwize V7000(也就是我们常说的V7000)是新推出的一款中端存储系统,这款系统的定位虽然在中端,但是Storwize V7000提供有存储管理功能,这一功能以前只有高端存储才拥有(例如 ...

  7. mongodb底层存储和索引原理——本质是文档数据库,无表设计,同时wiredTiger存储引擎支持文档级别的锁,MMAPv1引擎基于mmap,二级索引(二级是文档的存储位置信息『文件id + 文件内offset 』)

    MongoDB是面向文档的数据库管理系统DBMS(显然mongodb不是oracle那样的RDBMS,而仅仅是DBMS). 想想一下MySQL中没有任何关系型数据库的表,而由JSON类型的对象组成数据 ...

  8. Java并发之底层实现原理学习笔记

    本篇博文将介绍java并发底层的实现原理,我们知道java实现的并发操作最后肯定是由我们的CPU完成的,中间经历了将java源码编译成.class文件,然后进行加载,然后虚拟机执行引擎进行执行,解释为 ...

  9. iOS weak底层实现原理

    今年年底做了很多决定,离开工作三年的深圳,来到了上海,发现深圳和上海在苹果这方面还是差距有点大的,上海的市场8成使用swift编程,而深圳8成的使用OC,这点还是比较让准备来上海打拼的苹果工程师有点小 ...

随机推荐

  1. VMware CentOS LVM磁盘扩容

    一. 在虚拟机上增加磁盘空间 如下图. 增加完后会有提示 "磁盘已成功扩展.您必须从客户机操作系统内部对磁盘重新进行分区和扩展文件系统.是继续完成以下步骤才算成功. 二.调整虚拟机磁盘LVM ...

  2. Java 设计模式系列(十八)备忘录模式(Memento)

    Java 设计模式系列(十八)备忘录模式(Memento) 备忘录模式又叫做快照模式(Snapshot Pattern)或Token模式,是对象的行为模式.备忘录对象是一个用来存储另外一个对象内部状态 ...

  3. Android登录模块原理及实现

    1.需要保持登录状态,在本地保存登录的状态信息. 2.界面布局 3.登录流程 输入信息=>登录成功=>手机号发送验证码=>输入验证码=>登录成功

  4. Python中where()函数的用法

    where()的用法 首先强调一下,where()函数对于不同的输入,返回的只是不同的. 1当数组是一维数组时,返回的值是一维的索引,所以只有一组索引数组 2当数组是二维数组时,满足条件的数组值返回的 ...

  5. apicloud 和 微信小程序,你会用哪 个?

    微信 小程序开始火了,app跨平台的革命再次高涨,不得不说,不用再担心android和ios双版本开发成本,及h5的开发 和apicloud一样,不需要关注平台问题,只需要关注前端js.css就能大a ...

  6. (轉)CSS 单行溢出文本显示省略号...的方法(兼容IE FF)

    轉自:http://www.cnblogs.com/hlz789456123/archive/2009/02/18/1392972.html html代码:<div><p>&l ...

  7. 重拾C语言基础知识

    从实习到工作两年多的时间了,虽然感觉学到了很多知识,但是事实上却将立足之本的基础知识给忘了个精光.也许跟自己没有出去找工作有关,没有好好的将C语言的基础牢牢掌握. 从现在开始吧!好好的重温基础,做一名 ...

  8. Spring 框架简介

    Spring 是一个开源框架,是为了解决企业应用程序开发复杂性而创建的.框架的主要优势之一就是其分层架构,分层架构允许您选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架. 在这篇由三部 ...

  9. [label][Fireworks][转载] Web Slices - Fireworks CS5

    Web Slices – Fireworks CS5 http://bestwebdesignz.com/tips/fireworks/web-slices-fireworks-cs5/ Need a ...

  10. Buffer Pool--数据库内存结构

    数据库从操作系统申请到的内存可分为两部分:1.缓存池内存(数据页和空闲页)2.非缓存池内存(线程/DLL/连接服务器等) Note:通过服务器实例属性设置的最大/最小服务器内存是指缓存池内存Note: ...