概述
前面分析了leveldb中cache机制的实现,下面来分析下leveldb基于cache机制实现的两个不同功能的cache,分别是table cache和block cache。table cache缓存的是sstable的索引数据,类似于文件系统中对inode的缓存;block cache是缓存的block数据,block是sstable文件内组织数据的单位,也是从持久化存储中读取和写入的单位,类似于文件系统中文件块的缓存;
Cache* NewLRUCache(size_t capacity) {
return new ShardedLRUCache(capacity);
}
virtual Handle* Insert(const Slice& key, void* value, size_t charge,
void (*deleter)(const Slice& key, void* value)) {
上面是创建cache的接口,创建TableCache和block cache都是调用的该接口,只不过他们capacity的含义不同。
以及插入一个cache元素的接口,TableCache和block cache插入元素都是调用该接口,key代表插入元素的key值是多少,用于hash快速查找,value就是cache真正缓存的数据,charge表示该元素占cache容量的权重是多少。
capacity的含义是和charge相对应的。
TableCache
TableCache中的capacity代表的含义是最多能够缓存多少个sstable文件的索引信息,
tablecache默认容量是1000,意味这最多能缓存1000个sstable文件的索引信息。table cache的大小由options.max_open_files确定,其最小值为20-10,最大值为50000-10。
DBImpl::DBImpl(const Options& raw_options, const std::string& dbname)
{
...
const int table_cache_size = options_.max_open_files - kNumNonTableCacheFiles;
table_cache_ = new TableCache(dbname_, &options_, table_cache_size);
}
class TableCache {
...
Cache* cache_;
}
TableCache::TableCache(const std::string& dbname,
const Options* options,
int entries)
: env_(options->env),
dbname_(dbname),
options_(options),
cache_(NewLRUCache(entries)) {
//创建一个容量为entries的ShardedLRUCache(ShardedLRUCache请参见前面cache的分析
}
Status TableCache::FindTable(uint64_t file_number, uint64_t file_size,
Cache::Handle** handle) {
char buf[sizeof(file_number)];
EncodeFixed64(buf, file_number);
Slice key(buf, sizeof(buf));
*handle = cache_->Lookup(key);//可以看出tablecache的key就是文件的编号
if (*handle == NULL) {
//cache中没找到该文件对应的table
//先打开该文件,涉及系统调用,挺费时间的
std::string fname = TableFileName(dbname_, file_number);
RandomAccessFile* file = NULL;
Table* table = NULL;
s = env_->NewRandomAccessFile(fname, &file);
if (s.ok()) {
//打开文件成功,则创建该文件对应的table
s = Table::Open(*options_, file, file_size, &table);
}
TableAndFile* tf = new TableAndFile;
tf->file = file;
tf->table = table;
//tablecache中的value值就是TableAndFile,它包含一个已经打开的文件描述
//符,以及创建好的table,加入cache中
*handle = cache_->Insert(key, tf, 1, &DeleteEntry);
/*第三个参数size_t charge为1,charge表示该元素占cache容量的权重是多少,因为上面我们分析了
*TableCache中的capacity代表的含义是最多能够缓存多少个sstable文件的索引信息,新插入一个元素
*意味这能够缓存的sst文件数少了一个,因此这里的权重值charge为1
*/
}
return s;
}
BlockCache
BlockCache中的capacity代表的含义是最多能够缓存多少字节的block数据。
BlockCache默认容量是8M,意味最多能缓存8M的block数据。
Options SanitizeOptions(const std::string& dbname,//规范Options各个参数在合法范围
const InternalKeyComparator* icmp,
const InternalFilterPolicy* ipolicy,
const Options& src) {
Options result = src;
...
result.block_cache = NewLRUCache(8 << 20);
...
}
/*它根据参数指明的block,返回一个iterator对象,调用者就可以通过这个iterator对象遍历block存储的
k/v对,这其中用到了LRUCache。*/
Iterator* Table::BlockReader(void* arg, const ReadOptions& options,
const Slice& index_value)
{//从参数中解析出BlockHandle对象,其中arg就是Table对象,index_value存储的是
//BlockHandle对象(读取Block的索引)
Table* table = reinterpret_cast<Table*>(arg);
Cache* block_cache = table->rep_->options.block_cache;
Block* block = NULL;
Cache::Handle* cache_handle = NULL;
BlockHandle handle;
Slice input = index_value;
Status s = handle.DecodeFrom(&input);
// We intentionally allow extra stuff in index_value so that we
// can add more features in the future.
if (s.ok()) {
BlockContents contents;
//根据block handle,首先尝试从blockcache中直接取出block,不在blockcache中则
//调用ReadBlock从文件读取,读取成功后,根据option尝试将block加入到blockcache中。
//并在Insert的时候注册了释放函数DeleteCachedBlock。
if (block_cache != NULL) {
char cache_key_buffer[16];
EncodeFixed64(cache_key_buffer, table->rep_->cache_id);
EncodeFixed64(cache_key_buffer+8, handle.offset());
Slice key(cache_key_buffer, sizeof(cache_key_buffer));
cache_handle = block_cache->Lookup(key);//尝试从blockcache中直接取出block
if (cache_handle != NULL) {
block = reinterpret_cast<Block*>(block_cache->Value(cache_handle));
} else {
s = ReadBlock(table->rep_->file, options, handle, &contents);
if (s.ok()) {
block = new Block(contents);
if (contents.cachable && options.fill_cache) {
cache_handle = block_cache->Insert(
key, block, block->size(), &DeleteCachedBlock);
/*上面这条便是向blockcache插入一个新元素,可以看到value值是Block类(主要负责该block块的
*读操作),权重值charge代表的是该block块的大小*/
}
}
}
} else {
s = ReadBlock(table->rep_->file, options, handle, &contents);
if (s.ok()) {
block = new Block(contents);
}
}
}
//如果读取到了block,调用Block::NewIterator接口创建Iterator,如果cache handle为NULL,
//则注册DeleteBlock,否则注册ReleaseBlock,事后清理。
Iterator* iter;
if (block != NULL) {
iter = block->NewIterator(table->rep_->options.comparator);
if (cache_handle == NULL) {
iter->RegisterCleanup(&DeleteBlock, block, NULL);
} else {
iter->RegisterCleanup(&ReleaseBlock, block_cache, cache_handle);
}
} else {
iter = NewErrorIterator(s);
}
return iter;
}