Slice
作用
- Slice包括length和一个指向外部字节数组的指针。
- 相比返回std::tring,返回Slice的开销会小的多(没有拷贝,Slice中没有实际数据,只有指向数据的指针,开销低)。
- leveldb允许key和value包含’\0’。
兼容性
可以方便实现与std::string的互转。
注意事项
slice的数据是外部管理的,因此slice的使用者需要保证生命周期内外部数组是有效的。
Varint
作用
Varint是一种比较特殊的整数类型,它包含有Varint32和Varint64两种,它相比于int32和int64最大的特点是长度可变。
机制
-
varint是一种紧凑的表示数字的方法,它用一个或多个字节来表示一个数字,值越小的数字使用越少的字节数。采用Varint,对于很小的int32类型的数字,则可以用1个字节来表示。大的数字则可能需要5个字节来表示。
-
varint中的每个字节的最高位(bit)有特殊含义,如果该位为1,表示后续的字节也是这个数字的一部分,如果该位为0,则结束。其他的7位(bit)都表示数字。
7位能表示的最大数是127,因此小于128的数字都可以用一个字节表示。
大于等于128的数字,比如说300,会用两个字节在内存中表示为:
原始二进制: 100101100
编码后二进制: 10 10101100
低 高
10101100 00000010
InternalKey
作用
- 用户角度:使用slice作为key
- leveldb内部:使用InternalKey作为key
- 结构:[
Slice user_key
] + [SequenceNumber<<8 + ValueType
],后半部分固定64位,即8字节。
LookupKey
作用
当需要在leveldb查找对象的时候,查找顺序是从第0层到第n层遍历查找,找到为止(最新的修改或者删除的数据会优先被找到,所以不会出现一个键有多个值的情况)。由于不同层的键值不同,所以LookupKey提供了不同层所需的键值。
结构
Key结构:
A: klength varint32 <-- start_
B: userkey char[klength] <-- kstart_
C: tag uint64
<-- end_
A意义:user_key.size() + 8 变长编码后的值
B意义:userkey
C意义:64位整型顺序号<<8+值类型 64位定长编码后的值
memtable_key = A + B + C
internal_key = B + C
user_key = B
Comparator
作用
Comparator是个抽象类,主要用于数据键值比较的,毕竟leveldb是按照键有序存储的,所以比较器算是一个比较通用的类型。
注意
以上的用std::string作为类型,其实数据中可能存在’\0’。在很多情况需要用一些特殊的字符做分割,比如 std::string a=[‘a’, ‘b’, ‘c’, ‘\0’, ‘d’]
,a的长度是5而不是3。
BytewiseComparatorImpl
作用
- 因为Slice并没有规定Key具体类型,所以leveldb是支持用户自定义比较器的,在创建leveldb数据库对象的时候通过Option指定。
- BytewiseComparatorImpl是leveldb默认的比较器,基于二进制比较。默认配置相见
options.cc
。
InternalKeyComparator
作用
- 用于内部的Key比较器。
- cmp原则
- 1)userkey
- 2)seq大小,越大越新。
- FindShortestSeparator / FindShortSuccessor
- 1)提取userkey,通过userkey查找;
- 2)追加kMaxSequenceNumber + kValueTypeForSeek。
LevelDB 的整体架构
MemTable
内存数据结构,具体实现是 SkipList。 接受用户的读写请求,新的数据会先在这里写入。
Immutable MemTable
当 MemTable 的大小达到设定的阈值后,会被转换成 Immutable MemTable,只接受读操作,不再接受写操作,然后由后台线程 flush 到磁盘上 —— 这个过程称为 minor compaction。
Log
数据写入 MemTable 之前会先写日志,用于防止宕机导致 MemTable 的数据丢失。一个日志文件对应到一个 MemTable。
SSTable
Sorted String Table。分为 level-0 到 level-n 多层,每一层包含多个 SSTable,文件内数据有序。除了 level-0 之外,每一层内部的 SSTable 的 key 范围都不相交。
Manifest
Manifest 文件中记录 SSTable 在不同 level 的信息,包括每一层由哪些 SSTable,每个 SSTable 的文件大小、最大 key、最小 key 等信息。
Current
重启时,LevelDB 会重新生成 Manifest,所以 Manifest 文件可能同时存在多个,Current 记录的是当前使用的 Manifest 文件名。
TableCache
TableCache 用于缓存 SSTable 的文件描述符、索引和 filter。
BlockCache
SSTable 的数据是被组织成一个个 block。BlockCache 用于缓存这些 block(解压后)的数据。