Protobuf底层存储原理
底层二进制存储
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
- → 000 0001 ++ 001 0110 (drop the msb and reverse the groups of 7 bits)
由此看见,多字节的二进制存储,就是多了丢弃最后一位,翻转字节的步骤。
更多数据类型
有符号整型(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底层存储原理的更多相关文章
- HBase底层存储原理
HBase底层存储原理——我靠,和cassandra本质上没有区别啊!都是kv 列存储,只是一个是p2p另一个是集中式而已! 首先HBase不同于一般的关系数据库, 它是一个适合于非结构化数据存储的数 ...
- python-变量&底层存储原理
目录 1.变量 1.变量如何使用 2.变量存储的原理 --[ 重点 ] 3.变量存储要遵循印射关系 4.变量三要素 2.常量 3.底层优化 4.垃圾回收机制 1.变量 1.变量如何使用 1.什么是变量 ...
- HBase底层存储原理——我靠,和cassandra本质上没有区别啊!都是kv 列存储,只是一个是p2p另一个是集中式而已!
理解HBase(一个开源的Google的BigTable实际应用)最大的困难是HBase的数据结构概念究竟是什么?首先HBase不同于一般的关系数据库,它是一个适合于非结构化数据存储的数据库.另一个不 ...
- Redis数据的底层存储原理
redis底层是用什么结构来存储数据的呢? 我们从源码上去理解就会容易的多: redis底层是使用C语言来编写的,我们可以看到它的数据结构声明.一个 dict 有两个dictht,一个dictht ...
- 列式数据库~clickhouse 底层存储原理
简介:今天介绍列式数据库的一些基本原理 一 数据目录 Data目录 数据存储目录,数据按照part分成多个文件夹,每个文件夹下存储相应数据和对应的元信息文件 Metadata 表定义语句,存储所有表 ...
- V7000存储数据恢复_底层结构原理拆解及Mdisk磁盘掉线数据恢复方法
Storwize V7000(也就是我们常说的V7000)是新推出的一款中端存储系统,这款系统的定位虽然在中端,但是Storwize V7000提供有存储管理功能,这一功能以前只有高端存储才拥有(例如 ...
- mongodb底层存储和索引原理——本质是文档数据库,无表设计,同时wiredTiger存储引擎支持文档级别的锁,MMAPv1引擎基于mmap,二级索引(二级是文档的存储位置信息『文件id + 文件内offset 』)
MongoDB是面向文档的数据库管理系统DBMS(显然mongodb不是oracle那样的RDBMS,而仅仅是DBMS). 想想一下MySQL中没有任何关系型数据库的表,而由JSON类型的对象组成数据 ...
- Java并发之底层实现原理学习笔记
本篇博文将介绍java并发底层的实现原理,我们知道java实现的并发操作最后肯定是由我们的CPU完成的,中间经历了将java源码编译成.class文件,然后进行加载,然后虚拟机执行引擎进行执行,解释为 ...
- iOS weak底层实现原理
今年年底做了很多决定,离开工作三年的深圳,来到了上海,发现深圳和上海在苹果这方面还是差距有点大的,上海的市场8成使用swift编程,而深圳8成的使用OC,这点还是比较让准备来上海打拼的苹果工程师有点小 ...
随机推荐
- 20172325《Java程序设计》第一周学习总结
20172325<Java程序设计>第一周学习总结 教材学习内容总结 第一章 1.1软件质量 软件工程是一门关于高质量软件开发的技术和理论的学科. 高质量软件的特征 1.2 数据结构 软件 ...
- npm 更改为淘宝镜像的方法
1.命令行临时使用指定镜像(淘宝) npm --registry https://registry.npm.taobao.org install express 2.命令行永久更改使用指定镜像(淘宝) ...
- FW:程序在内存的划分(转)
一.预备知识—程序的内存分配 一个由c/C++编译的程序占用的内存分为以下几个部分 1.栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等.其操作方式类似于数据结构中的栈. ...
- android,gridview
package com.wes.gridview; import java.util.List; import android.content.Context; import android.cont ...
- win7搭建node+npm+bower的环境
原文的地址:https://my.oschina.net/JeeChou/blog/219699 Windows下的NodeJS安装是比较方便的(v0.6.0版本之后,支持windows native ...
- 从Objective-C到Swift 单例模式
在Objective-C中经常会用到单例模式.最常见的就是: [UIApplication sharedApplication].delegate 这里的sharedApplication就是一个返回 ...
- windows7 不能更新,提示:"WindowsUpdate_80240016" "WindowsUpdate_dt000",如何解决?
计算机(右键) ---- 管理 -------- 服务和应用程序 -----服务(找到名称为windows update的服务,并且在windwos update服务右键 选择重新启动 ) 再次安装更 ...
- 【StatLearn】统计学习中knn算法的实验(1)
Problem: Develop a k-NN classifier with Euclidean distance and simple voting Perform 5-fold cross va ...
- Python学习-12.Python的输入输出
在Python中,输出使用print函数,之前用过了. 输入的话,则使用input函数. var = input() print('you input is' + var) 输入haha则将输出you ...
- IDEA配置spring
大半天都在看spring,以前总是看不下去,这次慢慢来,慢慢看. 看那些基础的,倒是还不错.好多都是关于helloworld的,写完helloworld,觉得不怎么形象.于是写了动物,作为接口. (1 ...