LevelDB的SSTable数据存储格式--可以与rosbag对比

SSTable文件是memtable 数据到一定阈值写入文件形成的,由于内存容量总是有限的,将一定量数据写入磁盘可以存放更多数据,所以leveldb相比redis能存放更多数据。既然数据持久化到磁盘,那么还有必然涉及到从磁盘中查询数据,从磁盘中查询数据与从内存中查询数据的效率是不一样的,所以SSTable 数据组织方式必然与众不同,因为必须要提高查询效率,不能给一个key就去遍历所有SSTable。因此,本文的另一个目的就是学习SSTable 文件如何组织key-value,提高查询效率。为了提高内存中数据查询效率 我们学习了各种数据结构如红黑树,散列表,那么SSTable是学习如何提高文件查询数据效率的一个很好例子。

在学习源码之前还是先看看SSTable文件的结构 


整体上看 SSTable文件分为数据区与索引区,尾部的footer指出了meta index block与data index block的偏移与大小,index block指出了各data block的偏移与大小,metaindex block指出了filter block的偏移与大小。

1)data block:存储key-value记录,分为Data、type、CRC三部分 
2)filter block:默认没有使用,用于快速从data block 判断key-value是否存在 
3)metaindex block :记录filter block的相关信息 
4)Index block:描述一个data block,存储着对应data block的最大Key值,以及data block在文件中的偏移量和大小 
5)footer:索引的索引,记录metaindex block和Index block在SSTable中的偏移量了和大小

下面再具体看看各个部分物理结构

1、block 
sstable中data block 、metaindex block、index block都用这种block这种结构。对于data block,当block大小(record、restarts数组、以及num_restarts)超过4k时,就切换一个新的block继续往SSTable写数据,而metaindex block、index block就只有一个block,所以上图看起来data block有多个。 
 这里写图片描述
block主要由数据区record和restarts组成。 
为什么是这种结构? 
data block主要是存储数据,block内给一定数量(默认16)key-value分组,每组又用restarts数组记录起始位置,因此可以根据restarts读取每组起始位置key-value,由于block内的数据是从小到大有序存储的,所以可以通过restarts数组,获取每组起始key-value,比较起始key key(n)与查找的key大小,如果key(n)>key,那么key一定在序号>=n组之后,否则在 < n组之前。因此可以通过二分查找思想通过restarts获取起始key,来定位key的位置,避免线性查找低效。 
因此,restarts的思想就是提高block内key-value查找效率,直接定位key所在group。

下面再来看看record结构。record相对有意思,不是简单的用key-length | key-data | value-length|value-data存储。 
  这里写图片描述
 data block中的key是有序存储的,相邻的key之间可能有重复,因此存储时采用前缀压缩,后一个key只存储与前一个key不同的部分。重启点指出的位置即每组起始位置的key不按前缀压缩,而是完整存储该key。 
 type是表示数据是否压缩,以怎样的方式压缩,crc32是该block校验码,这个非本文分析重点。

2、index block 
index block 的结构也是block 结构,是data block的索引,记录每个data block 最大key 和 起始位置以及大小。具体的存储方式是以每个data block最大key 为key,以data block 起始位置和大小为value。因此可以根据每个block的最大key与查询key比较,直接定位查询key所在的位置。 
这是理论上key的存储方式,但是在sstable二次压缩的过程对key做了一个优化,它并不保存最大key,而是保存一个能分隔两个data block的最短Key,如:假定data block1的最大一个key为“abcdefg”,data block2最大key为“abzxcv”,则index可以记录data block1的索引key为“abd”;这样的分割串可以有很多,只要保证data block1中的所有Key都小于等于此索引,data block2中的所有Key都大于此索引即可。这种优化缩减了索引长度,查询时可以有效减小比较次数。 
 因此,index block的思想是提高SSTable内key-value查找效率,直接定位key所在block。

3、metaindex_block 
也是block结构。就只有一条记录,其key是filter. + filter_policy的name,value是filter大小和起始位置。

4、filter block 
filter block就是一个bloom filter,关于bloom filter原理概念可以百度。 
每个bloom filter是对data block 的key 经过hash num 次形成的字节数组,多少个data block对应多少个bloom filter。 
bloom filter实质就是一个bit 数组,对block 内key hash,将相应的位置设为1,这种设计关键在于能提高不存在的key判断效率,通过filter 计算,如果不存在,就不用通过data block内的restarts方式读取文件查找key是否存在,但是如果filter判断存在,还需通过restarts方式确定。 
5、footer 
footer位于SSTable文件尾部,占用空间固定为48个字节。其末尾8个字节是一个magic_number。metaindex_block_handle与index_block_handle物理上占用了40个字节,metaindex_block_handle和index_block_handle是BlockHandle数据类型, 这种结构用于记录metaindex block 和index block的起始位置和大小。 
 这里写图片描述
对于BlockHandle ,其实可以看作文件内容指针实现方式,BlockHandle记录数据位置及大小,与c/c++指针 思想类似,通过地址和大小可以读取数据。 
BlockHandle格式

varint64 offset | varint64 size_
采用变长存储,所以实际上存储可能连32字节都不到,剩余填充0。

总结,SSTable其实就是通过二次索引,先读取footer,根据footer中index_block_handler记录的index_block起始位置和大小,读取index block,通过index block 查询key所在data block,再在data block内部通过restarts 进一步确定key所在group。 
下面是完整的SSTable结构图 

这里写图片描述

对于rosbag,由于它涉及到多种类型数据的混合存储,面向的数据量更大,为此,将所有的索引数据都放到内存是不现实的,所以它的设计如下:

Header段+数据段1+针对数据段1的多个索引段+数据段2+针对数据段2的多个索引段+...+数据段k+针对数据段k的多个索引段+connection段+msgdefinition段+元数据段

其中元数据段类似sstable的footer,是数据段粒度的索引;

数据段存储具体的数据,这里,具体的数据包括了消息的定义,消息类型等信息,后面跟着的是真实的数据。其中真实的数据包括两部分:时间戳和真实数据。

而数据段对应的索引段有多个,是与数据段内存在的connection数量一致的,简单地讲,可以把一个connection当成一个topic。在数据段,所有topic的数据是混合存储的,当然是按照时序存储的,如果我们要查询某一个topic的数据,顺序读取所有的消息,然后根据是否是我们需要的topic来筛选,也是可以的,但是效率低。为此,在数据段之后,又按照topic对前面的数据段建立了索引,每个索引段只针对一个topic。这样,从索引段,就可以拿到记录粒度的数据了。这样,通过元数据段+索引段,就可以精确定位需要读的记录。当然,索引段数据的检索可能是顺序检索,也可以是二分检索。它是通过两级索引定位到具体的消息的。

对于rosbag类型的,始终在缓存中的是元数据段,connection段和msgdefinition段。而具体的数据段和索引段,只是保留当前数据段 及其对应的索引段。否则,试想一下,在ros中,消息源源不断地产生,消息类型多种多样,每一个消息都要对应一个索引,如果都放到内存里,那么开销可想而知。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值