LevelDB SSTable格式详解
作者:phylips@bmy
leveldb使用
2012-01-16
1.SSTable文件格式 (3)
1.1.格式说明 (3)
1.2.基本机制 (5)
1.2.1.数据压缩 (5)
1.2.2.Varint编码 (5)
1.2.3.CRC校验 (6)
1.2.4.前缀压缩 (10)
1.2.5.索引优化 (11)
1.3.几个问题 (12)
2.一个实际的SSTable文件 (13)
2.1.数据组成 (13)
2.2.二进制内容 (14)
3.单个文件读写过程 (15)
3.1.读文件 (15)
3.1.1.基本过程 (15)
3.1.2.代码分析 (16)
3.2.写文件 (17)
3.2.1.基本过程 (17)
3.2.2.代码分析 (17)
4.与HFile的对比分析 (18)
4.1.HFile V1文件格式 (20)
4.2.对比分析 (21)
5.性能因素 (21)
5.1.Block大小 (22)
5.2.重启点区间大小 (22)
5.3.压缩 (22)
5.4.CRC (22)
6.参考文献 (22)
7.附录 (23)
32c_defs.h (23)
_crc32ctable.c (24)
1.SSTable文件格式
SSTable是Sorted String Table的简称,也就是Bigtable底层的数据存储格式。SSTable文件是用来存储一系列有序的KeyValue对的,Key和Value都是字节串,KeyValue对根据固定比较规则有序地写入到文件中,文件内部分成一系列的Blocks(Block不会太大,常见的是64KB大小),同时具有必要的索引信息。这样就既可以顺序地读取内部KeyValue记录,也能够根据某个Key值进行快速定位。
Google开源的LevelDB对应了Bigtable中的tablet server,LevelDB的代码中自然也包含了SSTable这一重要结构。下面会对SSTable的格式进行详细地解说,同时还会就一些影响性能的关键点进行分析,并将它与开源类Bigtable系统HBase中的HFile进行一个对比。
1.1.格式说明
单个SSTable文件的格式如上图所示,文件由五大部分组成:Data Blocks,Meta Blocks,MetaIndexBlock,DataIndexBlock,Footer。除Footer部分外,其余都是一些block组成的结构,每个block则是由多个KeyValue组成的数据块。文件包含一些内部指针。每个这样的指针被称为一个BlockHandle,包含如下信息:  offset:    varint64
size: varint64
如图所示,Footer中会有一个meta index handle用来指向Meta Index Block,还有一个data index handle用来指向Data Index Block,然后这两个Index Block,实际上是一系列Data Blocks和Meta Blocks的索引,其内部的KeyValue值就包含了指向文件中的一系列Meta Block和Data Block的handle。
(1)文件内的key/value对序列有序排列,然后划分到一系列的data blocks里。这些blocks一个接一个的分布在文件的开头。每个data block会根据 里的代码进行格式化,然后进行可选地压缩。
(2)在数据blocks之后存储的一些meta blocks,目前支持的meta block类型会在下面进行描述。未来也可能添加更多的meta block类型。每个meta block也会根据里的代码进行格式化,然后进行可选地压缩。
(3) A "metaindex" block。会为每个meta block保存一条记录,记录的key值就是meta block的名称,value值就是指向该meta block的一个BlockHandle。(4) An "index" block。会为每个data block保存一条记录,key值是>=对应的data block里最后那个key值,同时在后面的那个data block第一个key值之前的那个key值,value值就是指向该meta block的一个BlockHandle。
(5) 文件的最后是一个定长的footer,包含了metaindex和index这两个block的BlockHandle,以及一个magic number。
metaindex_handle:    char[p];    // Block handle for metaindex
index_handle:    char[q]; // Block handle for index
padding:    char[40-p-q]; // 0 bytes to make fixed length
magic:        fixed64;    // == 0xdb4775248b80fb57
所以footer的总大小为40+8=48。
另需注意如下几点:
1.图中handle类型,也就是上面提到的BlockHandle,由offset和size组成
2.handle中的size大小不包括Type和CRC这两部分
3.CRC检验的内容包含了Type部分
4.block type目前有两种:0-不压缩,1-snappy压缩
需要着重解释的是:上图中除DataBlock外,其他类型的Block:MetaBlock,MetaIndexBlock,DataIndexBlock也都同属Block这一相同结构,都具有BlockData,Type,CRC这三部分,为简化起见,并没有画出这一级,只是画出了存在于这些Block中的KeyValue内容。同时对于BlockData来说,也是具有内部结构的,如上图中DataBlock部分画出的那样,BlockData又由如下几部分组成:KeyValue数据,Restart数组,RestartsNum。其中KeyValue部分结构如图顶部所示,Restart
数组和RestartsNum都是与前缀压缩相关的结构,Restart数组记录了重启点的偏移位置,RestartsNum则是重启点的个数,也即Restart数组的元素个数。它们实际上担当了BlockData内部数据索引的角,具体细节见下面的关于前缀压缩机制的分析。
1.2.基本机制
1.2.1.数据压缩
SSTable中的压缩是以Block为单位进行的。目前只支持一种压缩方式:Snappy,用户也可以选择不进行压缩。该压缩算法本身的实现并不在LevelDB内,用户如果使用的话需要首先安装Snappy,这是Google开源的一个压缩库。
当然,即使没有安装Snappy,LevelDB也是可以工作的。因为为了保证可移植性,LevelDB中对该压缩算法的调用也做了一层封装,如下:port/port_posix.h inline bool Snappy_Compress(const char* input, size_t length,
::std::string* output) {
#ifdef SNAPPY
output->resize(snappy::MaxCompressedLength(length));
size_t outlen;
snappy::RawCompress(input, length, &(*output)[0], &outlen);
output->resize(outlen);
return true;
#endif
return false;
}
这样如果没有Snappy库的话,也不会编译失败,即使用户在option中指定了采用压缩,压缩也不会生效,内部也只是采用非压缩格式。当然如果用户已经安装了Snappy,那么LevelDB的Makefile就能检测出来并定义好相关的宏。
1.2.2.Varint编码
1.2.2.1.基本原理
从上面图中可以看到,很多字段都是varint类型的。varint是对整数类型进行了变长编码。比如int32原本只有4字节,而编码后最短只需1个字节,最长需5个字节。在key,value长度都很小的情况下,采用varint编码的方式所带来的结构信息的空间节省会非常明显。
在varint编码中,编码后的每个字节的最高位用来表示后面的那个字节是否属于
当前的数,如果最高位为1表明,下一个字节也是当前数值的组成部分。这样对于一个varint来说除最后一个字节的最高位为0外,其他字节的高位都是1。
比如对于整数1,只需要一个字节存储:00000001。更复杂的比如400,它的二进制格式是00000001 10010000,按照7位一组就是:0000011 0010000假设存储是按照小端格式进行的,那么首先取出低7位(0010000)+1组成,10010000,然后取出下一个7位0000011,因为已经没有下一个非零值,所以编码后的结果就是:10010000 00000011。解码的过程刚好与之相反,去掉最高位后得出0010000 0000011,反转后得到最终结果:0000011 0010000。
1.2.2.2.代码分析
varint编解码代码在里。代码很清晰,此处不再详细解释。
1.2.3.CRC校验
1.2.3.1.基本原理
CRC校验的基本思想是利用线性编码理论,在发送端根据要传送一个n比特的帧或报文,发送器生成一个r比特的序列,称为帧检验序列(FCS)。这样所形成的帧将由(n+r)比特组成。这个帧刚好能被某个预先确定的数整除。接收器用相同的数去除外来的帧,如果无余数,则认为无差错。二进制码
多项式的加减运算为模2加减运算,即两个码多项式想加减时,对应项系数进行模2加减。所谓模2加减就是各位做不带进位、借位的加减。这种加减运算实际上就是逻辑上的异或运算,即加法和减法等价。更具体的介绍可参考:循环冗余校验码CRC原理详解。LevelDB中采用的是CRC-32C,其对应的生成多项式如下图所示:
1.2.3.2.代码分析
CRC相关的代码在,代码并不多,关键是理解其中的原理。计算的核心在于如何利用以前的计算结果,对新来的数据实现增量计算,而不是全部从头计算。这里采用了查表法来加速crc32值的计算,同时LevelDB内部的这个实

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。