目录
-
- 读操作的前期流程
- TableCache::Get
-
- [1 获取TableReader](#1 获取TableReader)
- [2 调用TableReader的Get方法](#2 调用TableReader的Get方法)
- 缓存的作用
- BlockCache&TableCache
- 参考资料
读操作的前期流程
对用户来说,读流程如下:
cpp
DB* db;
Options options;
std::string dbname = "/tmp/rocksdb4";
Status s = DB::Open(options, dbname, &db);
td::string myValue
db->Get(ReadOptions(), "a5", &myValue);
内部兜兜转转其实是走到了下面这个方法里(在db_impl.cc里)
cpp
Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
GetImplOptions& get_impl_options)
这里面大的流程就是先从mem读,如果没有就去imm读,如果还没有就去sst里面读。
处理从各层sst和缓存里面拿到kv的逻辑就在下面的代码里(实现在version_set.cc):
cpp
Version::Get(....)
Version::Get里面会按照L0的sst文件都检查一遍,然后之后的每层只用检查1个sst文件的逻辑进行搜索。
具体对某个sst文件的查询在Version::Get里面的TableCache::Get方法里。
对sst的查询在TableCache::Get里面!可以理解为每次查询某个sst的逻辑都包含了缓存。OK咱们这篇文章的重点就从这里开始。
TableCache::Get
我认为理解一个东西最好的逻辑,就是先假定它不存在,然后看看在没有它的情况整个流程如何运行,然后再去看看加上它都解决了那些问题。
如果咱们去掉所有的缓存逻辑,那么TableCache::Get里面就是两层
1 获取TableReader。
2 调用TableReader的Get方法。
1 获取TableReader
整体逻辑在TableCache::FindTable逻辑里面调用的TableCache::GetTableReader实现。
GetTableReader先通过PosixFileSystem构建了PosixMmapReadableFile,然后构建了RandomAccessFileReader,最终构建了TableReader。
这里有个问题TableReader是啥?它是什么作用?
慢慢说,我们知道一个sst的格式如下:
cpp
<beginning_of_file>
[data block 1]
[data block 2]
...
[data block N]
[meta block 1: filter block] (see section: "filter" Meta Block)
[meta block 2: stats block] (see section: "properties" Meta Block)
[meta block 3: compression dictionary block] (see section: "compression dictionary" Meta Block)
[meta block 4: range deletion block] (see section: "range deletion" Meta Block)
...
[meta block K: future extended block] (we may add more meta blocks in the future)
[metaindex block]
[index block]
[Footer] (fixed size; starts at file_size - sizeof(Footer))
<end_of_file>
读取一个sst的时候,就是先读取footer,然后根据footer定位到indeblock和metainde block
然后根据indexblock定位data block
根据meta indexblcok定位各个metablock (上面的结构代码里,包含了4种metablcok)
在BlockBasedTable::Open里面,就是按照如下的顺序解析各个block的。
cpp
// Read in the following order:
// 1. Footer
// 2. [metaindex block]
// 3. [meta block: properties]
// 4. [meta block: range deletion tombstone]
// 5. [meta block: compression dictionary]
// 6. [meta block: index]
// 7. [meta block: filter]
上面需要几次io?
举个例子,下面的流程就是解析index block的逻辑。
cpp
TableCache::GetTableReader
BlockBasedTableFactory::NewTableReader
BlockBasedTable::Open
BlockBasedTable::PrefetchIndexAndFilterBlocks
BlockBasedTable::CreateIndexReader
BinarySearchIndexReader::Create
BlockBasedTable::IndexReaderCommon::ReadIndexBlock
BlockBasedTable::RetrieveBlock
ReadBlockFromFile(define at block_based_table_reader.cc)
BlockFetcher::ReadBlockContents
BlockFetcher::GetBlockContents
换句话说,TableReader里面持有了一个sst里面的除了datablcok外的所有信息。
2 调用TableReader的Get方法
这里面的逻辑也简单,
1 先去布隆过滤器里面找。如果布隆过滤器说没有,那就肯定没有,直接返回;如果布隆过滤器说有,那就继续找。
2 从indexblock里面找到对应的datablcok,然后在datablcok上构建迭代器(这里需要读磁盘)
3 从datablock的迭代器里面,找到对应的kv。
4 检查kv是否已经删除,是否范围删除等等
缓存的作用
通过代码我们已经知道在TableCache::Get里面大的逻辑就是两个
1 获取TableReader(逻辑在TableCache::FindTable里)
2 调用TableReader的get方法
而TableCache::FindTable里面的逻辑就是如果从缓存里能拿到TableReader就直接返回,拿不到就按照前文的GetTableReader去磁盘里获取,获取后放到缓存里。
OK现在来看,咱们可以这么理解整个读流程里面的缓存
读的逻辑大的思路就是先读取sst的indexblock,然后通过indexblock去找到kv在哪个datablock,然后再去datablcok里面。(暂时不管布隆过滤器,range del的逻辑)
而缓存的作用就是把从sst里面的indexblock放到了内存里,不用每次都去读磁盘。
BlockCache&TableCache
咱们看以下两个参数
- cache_index_and_filter_blocks 表明是否把dataindex和布隆过滤器的放入block cache
- pin_l0_filter_and_index_blocks_in_cache 是否让level 0中的SST的data index和布隆过滤器常驻block cache。
纳尼?TableCache::FindTable里面代码层已经写死了TableReader放到缓存,怎么这里还有配置?
其实此缓存非必缓存!
前文说的存放TableReader的缓存叫tablecache。默认容量是4M。在DBImpl::DBImpl里面构造的cache,默认使用的是LRUCache。
而上面那两个参数说的是BlockCache,用户读取数据时拿到的datablock都会存到block cache里面。
请记得:block cache里面本来只存储sst的datablock
如果cache_index_and_filter_blocks 为ture,就相当于把所有sst的data index和布隆过滤器都放到block cache里。如果为false,那data index 每次都得从磁盘上读取
关于pin_l0_filter_and_index_blocks_in_cache为ture ,常驻的方案是,如果碰到了非L0的index blcok就清理掉,对于L0的index block 不清理。(在LRU Cache内部,如果目前容量还够,也不会进行实际的删除)
在rocksdb 6.15.5里面 pin_l0_filter_and_index_blocks_in_cache和cache_index_and_filter_blocks 默认都为false
在ceph17.2.5里面,rocksdb_cache_index_and_filter_blocks默认为true,rocksdb_pin_l0_filter_and_index_blocks_in_cache默认为false
参考资料
1 关于sst的格式