leveldb 文件格式
<文件开头>
[数据块 1]
[数据块 2]
...
[数据块 N]
[元数据块 1]
...
[元数据块 K]
[元数据索引块]
[数据索引块]
[脚注]        (固定大小; 偏移量为文件大小减去脚注大小)
<文件结束>

文件包含内部的指针. 每个指针可以用BlockHandle来表示, 包含以下信息

offset:   varint64
size:     varint64

varint64格式的说明请参考protocol-bufers/varints.

  1. key-value键值对序列被有序地分区存储在一系列的数据块中. 这些数据块从文件起始相继被存储. 每个数据块根据block_builder.cc的代码对key-value键值对进行编码, 并且有选择地被压缩.

  2. 我们在数据块后存储了一些元数据块. 目前被支持的元数据块类型在下面会得到详细说明, 更多的类型可能在未来得到支持. 每个元数据块也使用block_builder.cc进行编码, 并且有选择地被压缩.

  3. 元数据索引块为每一个元数据块创建一条key-value键值对, key是元数据块的名字, value是指向元数据块的内部指针.

  4. 数据索引块为每一个数据块创建一条key-value键值对, key在区间[last_key of current data block, first_key of successive data block)内, 取最短, value是指向数据块的内部指针.

  5. 文件尾部是一个固定长度的脚注, 包含了元数据索引块和数据索引块的BlockHandle以及一个magic number

     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;     // == 0xdb4775248b80fb57 (little-endian)
    

filter元数据块

如果数据库被创建时指定了FilterPolicy选项, 那么每一个table文件都会存储一个对应的filter元数据块. 而元数据索引块就会包含一条key-value键值对, key = "filter.<Name of FilterPolicy>", value为指向filter block的内部指针.

filter block存储了一系列的filters, filters_iFilterPolicy::CreateFilter()在某一数据块所有key作为输入时的输出, 该数据块在文件的偏移量落在区间[i * base, (i + 1) * base)内.
目前的base设置为2KB. 举例来说, 如果块X和块Y都在区间[i * base, (i + 1) * base - 1)内, 则块X和块Y的所有key都被转换到filter block的第ifilter.

filter block的格式如下:

[filter 0]
[filter 1]
[filter 2]
...
[filter N-1]

[offset of filter 0]                  : 4 bytes
[offset of filter 1]                  : 4 bytes
[offset of filter 2]                  : 4 bytes
...
[offset of filter N-1]                : 4 bytes

[offset of beginning of offset array] : 4 bytes
lg(base)                              : 1 byte

filter block尾部的offset array提供了一个高效的从数据块偏移量到对应的filter的映射.