RocksDB Get 流程
本文对 RocksDB 6.7.3 版本的 Get 流程进行分析。
概述
(1) 获取当前的 SuperVersion。SuperVersion 用于管理 CF 的元数据,如当前版本号、内存中的 MemTable 和 Immutable MemTable、SST 文件信息等:
1 | Struct SuperVersion { |
(2) 从内存读: 尝试从第一步 SuperVersion 中引用的 MemTable 以及Immutable MemTable 中获取对应的值
(3) 从持久化设备读: 首先通过 Table cache 获取到文件的元数据,如布隆过滤器(Bloom Filters)和数据块索引(Indexes), 如果 Block cache 中缓存了 SST 的数据块,如果命中那就直接读取成功,否则便需要从 SST 中读取数据块并插入到 Block cache
源码分析
DBImpl::Get
1 | Status DBImpl::Get(const ReadOptions& read_options, |
Rocksdb 的 Get 接口 DBImpl::Get 其实现主要靠 DBImpl::GetImpl 函数调用。
DBImpl::GetImpl
1 | Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key, |
DBImpl::GetImpl 获取 SuperVersion 的信息,如果用户未指定 snapshot,需要获取当前的 snapshot。读取时不对 key 加锁,能读到什么数据完全取决于 Options 传入的 snapshot。
SuperVersion 中按照数据的新旧程度排序 MemTable -> MemTableListVersion -> Version,依次按序查找,如果在新的数据中找到符合 snapshot 规则的结果,就可以立即返回,完成本次查找。
MemTable::Get
1 | bool MemTable::Get(const LookupKey& key, std::string* value, Status* s, |
利用 MemTableRep 的 Get 函数进行查找(以 SkipListRep 实现为例,在 skiplist 中进行查找,从 seek 到的位置开始向后遍历,遍历 entry 是否符合SaveValue 定义的规则)。SaveValue 函数查看当前 entry 是否还是当前查找的 key,如果不是则返回;查看当前 entry 的 snapshot 是否小于或等于需要查找的 snapshot,不符合则继续循环。如果 entry 的snapshot 符合上述条件,那么则跳出循环,返回查找结果。
MemTableListVersion::Get
1 | bool Get(const LookupKey& key, std::string* value, Status* s, |
MemTableListVersion 用链表的形式保存了所有 Immutable memtable 的结构,查找时,按时间序依次查找于每一个 memtable,如果任何一个 memtable 查找到结果则立即返回,即返回最新的返回值。具体 memtable 查找见上述 MemTable::Get 接口。
Version::Get
1 | void Version::Get(const ReadOptions& read_options, const LookupKey& k, |
GetNextFile 函数会遍历所有的 level,然后再遍历每个 level 的所有的文件,这里会对 level 0 的文件做一个特殊处理,这是因为只有 level 0 的 SST 的 range 不是有序的,因此我们每次查找需要查找所有的文件,也就是会一个个的遍历;而在非 level 0,我们只需要按照二分查找来得到对应的文件即可,如果二分查找不存在,那么我就需要进入下一个 level 进行查找。
调用 TableCache::Get 遍历单个 SST 文件,如果查找到结果立即返回。
TableCache::Get
1 | Status TableCache::Get(const ReadOptions& options, |
如果 row_cache 打开,首先它会计算 row cache 的 key,再在row cache 中进行一次查找,如果有对应的值则直接返回结果,否则则将会在对应的 SST 读取传递进来的 key。
调用 FindTable,进行对应 table_reader 的读取以及进行 Table cache。
接下来调用 t->Get,从 Block cache 或者 SST 中读取数据。
最后,如果 row_cache 打开,把读取的数据插入到 row cache 中。
BlockBasedTable::Get
1 | for (iiter->Seek(key); iiter->Valid()&&!done; iiter->Next()) { |
在 Table Cache 中,假设最终缓存的 table reader 是一个 BlockBasedTable 对象,调用 BlockBasedTable::Get。
首先,根据 Table 的元数据信息(布隆过滤器,数据块Index)查找 SST 内部的 Block。
调用 NewDataBlockIterator,若 Block 在 Block Cache 当中,直接返回对象地址,否则,发生磁盘IO,读取 SST 的 Block,构造 Block 对象并缓存其地址在 Block Cache 中。
找到 key 对应的 value,调用 get_context->SaveValue,直接将 Block 中的数据地址赋给用户传进来的 PinnableSlice* 中,减少了一次数据拷贝,并用引用计数避免 Block 被淘汰值被清除。
回顾
参考
RocksDB Get 流程