参考官网, 序列化原理

底层二进制存储

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. 修改 Windows 10 UWP 应用任务栏图标

    修改 Windows 10 UWP 应用任务栏图标 Windows 7 时代,修改任务栏图标很简单,右键打开属性,更改图标即可.但步入 Windows 8 之后,随着应用商店 UWP 应用的问世,可以 ...

  2. [原创]解决IPCH关闭后可能引发的无法调试的BUG

    前几天清理硬盘,发现大量IPCH目录,文件特别巨大,看着头大,所以决定改为临时模式,同时VS2015和VS2013我都改了,设置如下: (关键字:IPCH目录,VS大文件) 当设置好之后不会出现烦人的 ...

  3. Mysql 关于not exists一例

    场景: 业务上需要做一个查询,因为是Web及时响应,所以对响应时间有要求,原业务场景是需要从无库存订单中剔除绑定闲置库存,因单条sql查询实现复杂,故考虑用差集方式: select a.col1, a ...

  4. Mac Sublime Text 浏览器 设置快捷键 让html文件在浏览器打开

    首先,进入命令行界面(这里mac和windows一样) 应用命令下载插件 输入命令二 然后就会发现在工具栏多出一个下拉选项 mac的跟它有些区别就是快捷键 mac快捷键是^+√+ V ++字母V,就会 ...

  5. 一文读懂 超简单的spark structured stream 源码解读

    为了让大家理解structured stream的运行流程,我将根据一个代码例子,讲述structured stream的基本运行流程和原理. 下面是一段简单的代码: val spark = Spar ...

  6. Linux下编译busybox时出现的问题

    编译busybox的时候出现了一个问题: sync.c:(.text.sync_main+0x78): undefined reference to `syncfs' collect2: ld ret ...

  7. java反射简单实例

    这篇博友的总结的反射知识点是比较全面的 http://www.cnblogs.com/rollenholt/archive/2011/09/02/2163758.html 下面介绍我用反射做的两个功能 ...

  8. URAL1991 The battle near the swamp 2017-04-12 18:07 92人阅读 评论(0) 收藏

    The battle near the swamp Gungan: Jar Jar, usen da booma!  Jar Jar: What? Mesa no have a booma!  Gun ...

  9. IDEA 配置SSH2

    系统换成了mac os,因为喜欢它的界面体验,同时受不了win下面系统对硬盘的疯狂访问.发现在mac下面,IDEA真的不错,速度上快,并且它的智能提示真的很厉害.但是导入一个myeclipse的ssh ...

  10. 原创:MVC 5 实例教程(MvcMovieStore 新概念版:mvc5.0,EF6.01) - 1、初露锋芒

    说明:MvcMovieStore项目已经发布上线,想了解最新版本功能请登录 MVC 影视(MvcMovie.cn) 进行查阅.如需转载,请注明出处:http://www.cnblogs.com/Dod ...