rocksdb的缓存在读流程里面的作用

目录

读操作的前期流程

对用户来说,读流程如下:

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的格式

https://www.jianshu.com/p/d6ce3593a69e

https://www.cnblogs.com/lygin/p/17103522.html

相关推荐
起名字真南16 分钟前
【OJ题解】C++实现字符串大数相乘:无BigInteger库的字符串乘积解决方案
开发语言·c++·leetcode
少年负剑去16 分钟前
第十五届蓝桥杯C/C++B组题解——数字接龙
c语言·c++·蓝桥杯
cleveryuoyuo17 分钟前
AVL树的旋转
c++
神仙别闹40 分钟前
基于MFC实现的赛车游戏
c++·游戏·mfc
小c君tt1 小时前
MFC中 error C2440错误分析及解决方法
c++·mfc
木向1 小时前
leetcode92:反转链表||
数据结构·c++·算法·leetcode·链表
阿阿越1 小时前
算法每日练 -- 双指针篇(持续更新中)
数据结构·c++·算法
hunandede2 小时前
FFmpeg存放压缩后的音视频数据的结构体:AVPacket简介,结构体,函数
c++
hunandede2 小时前
FFmpeg 4.3 音视频-多路H265监控录放C++开发十三:将AVFrame转换成AVPacket。视频编码,AVPacket 重要函数,结构体成员学习
c++·ffmpeg·音视频
PGCCC4 小时前
【PGCCC】postgresql 缓存池并发设计
数据库·缓存·postgresql