LevelDB源码阅读(1)—— SSTable的生成

leveldb会按照不同版本组织数据(level-0 -> level-n,从新到旧),这些数据以SSTable格式存储于磁盘上。一个SSTable文件可以看成一个基于磁盘、只读的map,支持顺序扫描,同时可以查找某个key。本文就来探究一下SSTable文件的格式,以及创建过程。

1. SSTable文件格式

| - - - - - - - - - - - - - - - - |

|   Data Blocks          |

| - - - - - - - - - - - - - - - - |

|   Filter Block           |

| - - - - - - - - - - - - - - - - |

|    Meta Index Block     |

| - - - - - - - - - - - - - - - - |

|    Index Block      |

| - - - - - - - - - - - - - - - - |

   Footer        |

| - - - - - - - - - - - - - - - - |


如上就是SSTable的文件格式,整个文件包括一系列的Block,Block是磁盘io以及内存cache的基本单位,通过Block读写可以均摊每次IO的开销,利用局部性原理。Block的大小是用户可配置的。

1) Data Blocks:存放kv对,即实际的数据。kv对会按照大小划分成Block,无法保证所有的Block大小一致,但基本接近于配置的Block大小,但是当kv对数据大于该值时,会作为一个Block。Block内部还包括其他的一些元数据,后文会深入介绍。

2) Filter Block: Filter用于确定SSTable是否包含一个key,从而在查找该key时,避免磁盘IO。Filter Block用于存储Filter序列化后的结果。

3) Meta Index Block: 用于存放元数据,目前只会kv格式存储Filter block的偏移量,key是filter.${FILTER_NAME},value是filter block在文件的偏移量。

4) Index Block: 对Data block的索引,保存了各个Block中最小key,从而确定了Block的key的范围,加速某个key的搜索。

5) Footer: 元数据的元数据,其中包含Index Block和Meta Index Block的偏移量。Footer之所以在最后,是因为文件生成时是顺序追加的,而Footer的信息又依赖于之前的所有信息,所以只能在最后。由于包含了元数据,所以读取SSTable时首要的就是加载footer。


2. 数据结构

BlockHandle: 指向文件中的一个Block,有两个属性Block的偏移量(offset_)和大小(size_)。

Footer: 表示SSTable文件的Footer,大小固定。

这两个结构提供到string的序列化和反序列化的方法。

TableBuilder: 构造SSTable的入口。将一系列的kv对构造成SSTable。

BlockBuilder: 构造Block,对添加的kv对进行序列化。

FilterBlockBuilder: 构造Filter Block。

以上便是生成SSTable文件的主要数据结构。


3. BlockBuidler

上文提到Block是数据传输的基本单位,在Data Block中通过Block将连续的kv对打包处理,可以利用局部性原理。同时kv按key顺序存储,那么同一个Block中key的重复内容比例会增加,可以通过压缩提高空间利用率。

1) Block格式:

| - - Block Content: var len - - | - - Block Type: 1Byte - - | - - CRC: 4Byte - - |

一个Block包含3部分:

(1)Block Content:kv序列化后的内容,是可变长度的字节数组。

(2)Block Type:指定Block是否压缩,1个字节。

(3)CRC:CRC校验码,用于数据完整测试,4个字节。

2) Block Content格式:

| - - KV Entries: var len - - | - - Restart Point Array: 4nBytes - - | - - Num Restart Point: 4Byte - - |

(1)KV Entries:一系列kv内容,Block的实际内容,是可变长度的字节数组。

(2)Restart Point Array:Restart Point数组,每个元素就是一个整形,4n字节。后文会介绍Restart Point的作用。

(3)Num Restart Point:指定了Restart Point数组的长度,4字节。
3)  KV Entry格式:

由于kv对按key顺序存储,所以对key采用前缀压缩以节省空间。

| - - Shared: 4Byte - - | - - Non-shared: 4Byte - - | - - Val Len: 4Byte - - | - - Key Delta: var len - - | - - Value: var len - - |

(1)Shared:与前一个key共同前缀长度。

(2)Non-shared:key非共同前缀长度,Shared + Non-shared等于key的长度。

(3)Val len:value的长度。

(4)Key Delta:存储去除共同前缀的key。

(5)Value:存放Value。

通过上面描述可以知道,在存储一个key时,不会存其与前一个key的共同前缀,只会存不同的部分。那么,从磁盘读取一个key时,就需要先把前一个key读出才能获取完整的可以。这会存在一个问题,如果读取最后一个key,那么需要把[1, n-1]个key都读出才能获得完整内容。

LevelDB通过引入Restart Point来解决上述问题,每个Restart Point相当于前缀压缩的重启点。Restart Point指向一个key,它会存储完整的内容,其Shared等于0。通过在一些kv中平均插入多个Restart Point,可以减少前缀解压缩读取的长度。默认,LevelDB中放置Restart Point的间隔为16,保证最坏情况下最多只要读取15个key就能获取一个key的完整内容。

同时,Restart Point指向的key也是排序的,可以把底层kv序列的二级索引,在进行key搜索时,先进行Restart Point的二分查找框定范围,然后再在指定的key范围内线性查找。


4. FilterBlock

Filter用于加快key搜索,避免无效的磁盘IO。LevelDB本身提供Bloom Filter。可以简单把Filter当做是一个集合,用于判断一个key是否存在于该集合。FilterBlock中存放的是SSTable文件中所有key组成的集合信息(就是Filter根据key进行序列化后的结果)。

1) FilterBlock格式:

| - - Encoded Filter Array: var len - - | - - Filter Offset Array: 4nByte - - | - - Offset of Offset Array: 4Byte - - | - - Base lg: 1Byte -- |

(1)Encoded Filter Array:经过Filter序列化的字节数组,由n个Filter的信息组成。

(2)Filter Offset Array:指定每个Filter在Encoded Filter Array中的偏移量。

(3)Offset of Offset Array:Filter Offset Array的偏移量。

(4)Base lg:2^(Base lg)是对数据块构造Filter的最小size。LevelDB默认是2KB。

2) 流程:

LevelDB对于创建Filter源数据的大小有要求,不能小于2KB,这么做是为了防止源数据过小,导致取Filter的粒度过小,单位Block对应Filter的空间使用率过大,会比较浪费。

Encoded Filter Array与Data Block之间的对应关系稍微复杂些,当Block小于2KB时(比如1KB),那么多个Block会使用一个Filter。如果Block大于2KB(比如4KB),那么一个Block对应一个Filter。

在每次开始一个新的Block时,会调用FilterBuilder.StartBlock()方法,这里会确定Block与Filter之间的对应关系,如果前一个Filter已经完成,会生成这个Filter。在所有Key添加完毕后,会调用FilterBuilder.Finish()方法进行整体序列化,并返回序列化后的结果。


5. TableBuilder

TableBuilder是对外生成SSTable的接口,通过Add方法接收一个个kv,依托于底层的BlockBuidler创建Block,如果当前的Block大小超过预设的值,会调用BlockBuilder的Finish方法进行序列化,然后追加到文件。在所有kv添加完毕后,调用TableBuilder.Finish方法追加元数据,包括Filter Block,Meta Index Block,Index Block以及Footer。

6. 总结

上述介绍的是SSTable的磁盘表现形式,设计的相当精巧,包括但不限于前缀压缩,Restart Point的引入,Filter的源数据块划分等。下一篇会介绍SSTable的读取,即内存表现形式。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值