1

参考:

SST File Format

之前在LSM-Tree部分有提过,但当时了解的比较浅

<beginning_of_file>
[data block 1]
[data block 2]
...
[data block N]
[meta block 1: filter block]                  
[meta block 2: index block]
[meta block 3: compression dictionary block]  
[meta block 4: range deletion block]          
[meta block 5: stats block]                   
...
[meta block K: future extended block]  (More meta blocks may be added in the future)
[metaindex block]
[Footer]                               (fixed size; starts at file_size - sizeof(Footer))
<end_of_file>

metaindex block和index block

metaindex block记录了每个meta block的偏移和长度. key是meta block的名字, value的类型是 BlockHandle, 其定义如下

class BlockHandle {
 public:
  BlockHandle();
  BlockHandle(uint64_t offset, uint64_t size);
  ...
 private:
  uint64_t offset_;
  uint64_t size_;
}

index block记录了每个data block的索引. key是一个string, 它的值大于或等于被索引的block的最后一个key, 小于下一个data block的第一个key.
value是data block对应的BlockHandle.

Footer

Footer的大小固定为48字节

metaindex_handle: char[p]; // Block handle for metaindex
index_handle: char[q]; // Block handle for index
padding: char[40-p-q]; // zeroed bytes to make fixed length
// (40==2*BlockHandle::kMaxEncodedLength)
magic: fixed64; // 0x88e241b785f4cff7 (little-endian)

padding为空白字节,用于对齐
magic number占用8个字节,是个固定数值,用于读取时校验是否跟填充时相同,不相同的话就表示此文件不是一个SSTable文件

DataBlockIndex

DataBlockIndex包含DataBlock索引信息,用于快速定位到包含特定key的DataBlock;DataBlockIndex首先是一个block,因此包含三部分KeyValue、Type(固定1字节)、CRC检验码(固定4字节);Type标识该部分数据是否采用压缩算法,CRC是KeyValue + Type的检验码;key的取值是大于等于其索引block的最大key,并且小于下一个block的最小key;value也是BlockHandle类型,由变长的offset和size组成。

key的取值

为了节省空间,key的取值并不一定为其索引block的最大key,而是选取介于该block最大key与下一个block最小key之间的值。
假设其索引的block的最大key为"acknowledge",下一个block最小的key为"apple",如果DataBlockIndex的key采用其索引block的最大key,占用长度为len("acknowledge");采用后一种方式,key值可以为"ad"("acknowledge" < "ad" < "apple"),长度仅为2,并且检索效果是一样的。

DataBlock

DataBlock是KeyValue数据存储块,跟DataBlockIndex一样包含三部分:KeyValue、Type、CRC校验;

前缀压缩

每一个KeyValue记录都是一条Entry。
每条Entry的格式为:

  • shared_bytes:和前一个key相同的前缀长度。
  • unshared_bytes:和前一个key不同的后缀部分的长度。
  • value_length:value数据的长度。
  • key_delta:和前一个key不同的后缀部分。
  • value:value数据。

DataBlock中key的存储采用了前缀压缩机制,对于key的相同前缀,尽量只存储一次以节省空间。但是对于SSTable来说,它并没有对整个block的所有key进行一次性地前缀压缩,而是设置了很多区段,处于同一区段的key进行一次前缀压缩,每个区段的起点就是一个重启点。之所以设置很多区段是为了更好的支持随机读取,这样就可以在block内部对重启点进行二分,然后再在单个区段内遍历即可找到对应key值的记录,所以分段压缩就承担了block内部二级索引的功能。

在KeyValue部分的尾部用一个数组记录这些重启点的offset(固定四个字节),同时block的最后4个字节被固定用来保存重启点的个数。
截屏2022-01-22 20.29.40.png

Data Block默认每16个key会有一个重启点,Index Block默认每一个key都是重启点

  // Number of keys between restart points for delta encoding of keys.
  // This parameter can be changed dynamically.  Most clients should
  // leave this parameter alone.  The minimum value allowed is 1.  Any smaller
  // value will be silently overwritten with 1.
  int block_restart_interval = 16;

  // Same as block_restart_interval but used for the index block.
  int index_block_restart_interval = 1;

与BlockBasedTableBuilder相关的类

  • BlockBasedTable, 该类封装了用于读取磁盘BlockBasedTable类型的SST表的逻辑。
  • BlockBasedTableBuilder, 该类用于在磁盘上构建一个BlockBasedTable类型的SST表。
  • BlockBasedTableFactory, 该类是BlockBasedTable工厂方法的实现,用于创建BlockBasedTable/BlockBasedTableBuilder。

Write Block的逻辑:

BlockBasedTableBuilder的Add逻辑

  1. 判断key的类型,不同类型有不同的处理方法
  2. 判断当前的key是否比上一个key大,保证有序
  3. 判断是否需要flush当前的block到文件中,并清空data block
  4. add到data block中(data block的变量类型为BlockBuilder)

BlockBuilder的Add逻辑

  1. 判断是否需要restart point
  2. 如果不需要restart point,将当前插入的key与前一个key比较前缀,得到可以压缩的前缀长度。
  3. 得到所有需要的数据后,按照一个entry的格式,append到buffer中。

当向一个block中加入了若干个kv,由r->flush_block_policy来决定是否调用BlockBasedTableBuilder的Flush方法将当前的block写入到文件中,并清空block,重新再用。
默认的flush block策略定义在FlushBlockBySizePolicy中,即根据block的已写入大小来决定刷盘,接口为Update,默认的block大小为4K。

当需要flush block时,调用Flush方法,Flush方法调用了WriteBlock方法。

WriteBlock会首先调用data_block的Finish()方法,将start points append到buffer_中,设置block的标志位finished_ = true

然后会以下格式将一个block写入目标文件中

| block_data(uint8[n]) | type(uint8) | crc(uint32)|

在data block写完之后, 会在BlockBasedTableBuilder的Finish方法中,进行write meta block

Write Meta Block的顺序

Write meta blocks, metaindex block and footer in the following order.

  1. [meta block: filter]
  2. [meta block: index]
  3. [meta block: compression dictionary]
  4. [meta block: range deletion tombstone]
  5. [meta block: properties]
  6. [metaindex block]
  7. Footer

1iin
4 声望3 粉丝

Move on.