leveldb:TableCache以及BlockCache

概述

前面分析了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(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_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;
}

你可能感兴趣的:(造轮子之leveldb)