leveldb 源码分析 11¶
本系列《leveldb 源码分析》共有 22 篇文章,这是第十一篇
7.TableCache¶
这章的内容比较简单,篇幅也不长。
7.1 TableCache 简介¶
TableCache 缓存 的是 Table 对象,每个 DB 一个,它内部使用一个 LRUCache 缓存所有的 table 对象,实际上其内容是文件编号 {file number, TableAndFile}。TableAndFile 是一个拥有2 个变量的结构体:RandomAccessFile 和 Table*;
TableCache 类的 主要成员变量 有:
Env* const env_; // 用来操作文件
const std::string dbname_; // db名
Cache* cache_; // LRUCache
三个函数接口,其中的参数@file_number是文件编号,@file_size是文件大小:
void Evict(uint64_tfile_number);
// 该函数用以清除指定文件所有cache的entry,
//函数实现很简单,就是根据file number清除cache对象。
EncodeFixed64(buf,file_number); cache_->Erase(Slice(buf, sizeof(buf)));
Iterator* NewIterator(constReadOptions& options, uint64_t file_number,
uint64_t file_size, Table**tableptr = NULL);
//该函数为指定的file返回一个iterator(对应的文件长度必须是"file_size"字节).
//如果tableptr不是NULL,那么tableptr保存的是底层的Table指针。
//返回的tableptr是cache拥有的,不能被删除,生命周期同返回的iterator
Status Get(constReadOptions& options,
uint64_t file_number,uint64_t file_size,
const Slice& k,void* arg,
void(*handle_result)(void*, const Slice&, const Slice&));
// 这是一个查找函数,如果在指定文件中seek 到internal key "k" 找到一个entry,
//就调用 (*handle_result)(arg,found_key, found_value).
7.2 TableCache::Get()¶
先来看看Get 接口,只有几行代码:
Cache::Handle* handle = NULL;
Status s =FindTable(file_number, file_size, &handle);
if (s.ok())
{
Table* t =reinterpret_cast<TableAndFile*>(cache_->Value(handle))->table;
s = t->InternalGet(options,k, arg, saver);
cache_->Release(handle);
}
return s;
首先 根据 file_number 找到 Table 的 cache 对象,如果 找到了就调用 Table::InternalGet,对查找结果的处理在调用者传入的 saver 回调函数 中。 Cache 在 Lookup 找到 cache 对象 后,如果 不再使用需要调用 Release 减引用计数。这个见 Cache 的接口说明。
7.3 TableCache 遍历¶
函数 NewIterator(),返回一个可以遍历 Table 对象的 Iterator 指针,函数逻辑:
S1 初始化 tableptr,调用 FindTable,返回 cache 对象¶
if (tableptr != NULL) *tableptr =NULL;
Cache::Handle* handle = NULL;
Status s =FindTable(file_number, file_size, &handle);
if (!s.ok()) returnNewErrorIterator(s);
S2 从 cache 对象中取出 Table 对象指针,调用其 NewIterator 返回 Iterator 对象,并为 Iterator 注册一个 cleanup 函数。¶
Table* table =reinterpret_cast<TableAndFile*>(cache_->Value(handle))->table;
Iterator* result =table->NewIterator(options);
result->RegisterCleanup(&UnrefEntry, cache_, handle);
if (tableptr != NULL) *tableptr= table;
return result;
7.4 TableCache::FindTable()¶
前面的 遍历和 Get 函数 都依赖于 FindTable 这个私有函数完成对 cache 的查找,下面就来看看该函数的逻辑。函数声明为:
Status FindTable(uint64_t file_number, uint64_t file_size,
Cache::Handle** handle)
函数流程为:
S1 首先根据 file number 从 cache 中查找 table,找到就直接返回成功。¶
char buf[sizeof(file_number)];
EncodeFixed64(buf, file_number);
Slice key(buf, sizeof(buf));
*handle = cache_->Lookup(key);
S2 如果没有找到,说明 table 不在 cache 中,则根据 file number 和 db name 打开一个 RadomAccessFile。Table 文件格式为:..sst。如果文件打开成功,则调用 Table::Open 读取 sstable 文件。¶
std::string fname =TableFileName(dbname_, file_number);
RandomAccessFile* file = NULL;
Table* table = NULL;
s =env_->NewRandomAccessFile(fname, &file);
if (s.ok()) s =Table::Open(*options_, file, file_size, &table);
S3 如果 Table::Open 成功则,插入到 Cache 中。¶
TableAndFile* tf = newTableAndFile(table, file);
*handle = cache_->Insert(key,tf, 1, &DeleteEntry);
如果失败,则 删除 file,直接返回失败,失败的结果是不会 cache 的。
7.5 辅助函数¶
有点啰嗦,不过还是写一下吧。其中一个是为 LRUCache 注册的删除函数 DeleteEntry。
static void DeleteEntry(const Slice& key, void* value)
{
TableAndFile* tf =reinterpret_cast<TableAndFile*>(value);
delete tf->table;
delete tf->file;
delete tf;
}
另外一个是 为 Iterator 注册的清除函数 UnrefEntry。
static void UnrefEntry(void* arg1, void* arg2)
{
Cache* cache =reinterpret_cast<Cache*>(arg1);
Cache::Handle* h =reinterpret_cast<Cache::Handle*>(arg2);
cache->Release(h);
}