leveldb源码分析:数据查询

leveldb数据查询

查询的示例代码如下:

string res;
status = db->Get(ReadOptions(), "KeyNameExample", &res);

本文就先分析一下数据的获取流程。

db->Get获取数据

主要就是调用db的Get方法来查找数据;

Status DBImpl::Get(const ReadOptions& options, const Slice& key,
                   std::string* value) {
  Status s;
  MutexLock l(&mutex_);
  SequenceNumber snapshot;
  if (options.snapshot != nullptr) {                                  // 检查快照是否为空指针
    snapshot =
        static_cast<const SnapshotImpl*>(options.snapshot)->sequence_number();   // 获取快照对应的序列号
  } else {
    snapshot = versions_->LastSequence();                                        // 否则就获取版本最新的序列号
  }

  MemTable* mem = mem_;                                                           // 当前memtable内容
  MemTable* imm = imm_;                                                           // 不可变内容
  Version* current = versions_->current();                                        // 获取当前的版本
  mem->Ref();
  if (imm != nullptr) imm->Ref();
  current->Ref();

  bool have_stat_update = false;                                                  // 是否有更新标志位 设置为False
  Version::GetStats stats;

  // Unlock while reading from files and memtables
  {
    mutex_.Unlock();                                                              // 获取锁
    // First look in the memtable, then in the immutable memtable (if any).
    LookupKey lkey(key, snapshot);                                                // 将内容包装成LookupKey实例
    if (mem->Get(lkey, value, &s)) {                                              // 先在mem当中查找key
      // Done
    } else if (imm != nullptr && imm->Get(lkey, value, &s)) {                     // 如果在mem中没有查找到该key则在imm中查找数据
      // Done
    } else {
      s = current->Get(options, lkey, value, &stats);                             // 最后再文件中查找 即level层级的数据块中查找
      have_stat_update = true;                                                    // 此时设置更新为true
    }
    mutex_.Lock();
  }

  if (have_stat_update && current->UpdateStats(stats)) {                          // 如果在level层级文件中查找 并且当前的内容有更改则调用合并
    MaybeScheduleCompaction();
  }
  mem->Unref();                                                                   // 引用计数恢复
  if (imm != nullptr) imm->Unref();
  current->Unref();
  return s;                                                                       // 返回状态
}

从执行流程可知,获取数据时的优先级主要就是三个;

  1. 从当前内存memTable中获取;
  2. 如果第一步未获取到,则从当前的不可修改的imm中获取;
  3. 如果第二步未获取到,则从level层中去获取数据;
从memeTable中查找
mem->Get(lkey, value, &s)

此时调用的就是mem的Get方法来查找;

bool MemTable::Get(const LookupKey& key, std::string* value, Status* s) {
  Slice memkey = key.memtable_key();                                          // 先获取key的数据
  Table::Iterator iter(&table_);                                              // 生成table的迭代器
  iter.Seek(memkey.data());                                                   // 查找数据 
  if (iter.Valid()) {                                                         // 如果找到
    // entry format is:
    //    klength  varint32
    //    userkey  char[klength]
    //    tag      uint64
    //    vlength  varint32
    //    value    char[vlength]
    // Check that it belongs to same user key.  We do not check the
    // sequence number since the Seek() call above should have skipped
    // all entries with overly large sequence numbers.                        // 获取整个数据
    const char* entry = iter.key();
    uint32_t key_length;
    const char* key_ptr = GetVarint32Ptr(entry, entry + 5, &key_length);      // 获取指针内容值
    if (comparator_.comparator.user_comparator()->Compare(
            Slice(key_ptr, key_length - 8), key.user_key()) == 0) {           // key内容是否相同
      // Correct user key
      const uint64_t tag = DecodeFixed64(key_ptr + key_length - 8);           // 获取该key的标志位  是删除还是新增数据
      switch (static_cast<ValueType>(tag & 0xff)) {
        case kTypeValue: {
          Slice v = GetLengthPrefixedSlice(key_ptr + key_length);             // 如果是未删除数据
          value->assign(v.data(), v.size());                                  // 设置到velue中并返回
          return true;
        }
        case kTypeDeletion:
          *s = Status::NotFound(Slice());                                     // 如果该数据为删除则标记为NotFound
          return true;
      }
    }
  }
  return false;
}

其中有关iter.Seek的方法,本质上其实就是调用的是table_.Seek方法,而table_又是SkipList类型所以最终调用的是FindGreaterOrEqual;

template <typename Key, class Comparator>
inline void SkipList<Key, Comparator>::Iterator::Seek(const Key& target) {
  node_ = list_->FindGreaterOrEqual(target, nullptr);
}

template <typename Key, class Comparator>
typename SkipList<Key, Comparator>::Node*
SkipList<Key, Comparator>::FindGreaterOrEqual(const Key& key,
                                              Node** prev) const {
  Node* x = head_;                                      // 获取头部
  int level = GetMaxHeight() - 1;                       // 获取层级
  while (true) {
    Node* next = x->Next(level);                        // 依次遍历下一级
    if (KeyIsAfterNode(key, next)) {                    // 检查当前key的大小是否大于next的key大小
      // Keep searching in this list
      x = next;                                         // 如果是之后则继续深入
    } else {
      if (prev != nullptr) prev[level] = x;             // 如果指向不为空  且当前是最小数据长度  则 设置成头指针
      if (level == 0) {                                 // 如果为零就返回当前查找到的 否则下一个层级查找
        return next;                                    
      } else {
        // Switch to next list
        level--;
      }
    }
  }
}

在mem中查找的过程其实就和数据插入的过程比较类似。因为imm不可变table查找方式与该流程一样故不再叙述。

从level文件中查找
s = current->Get(options, lkey, value, &stats)

此时current其实就是Version对象,此时就是调用的Version的Get方法;

Status Version::Get(const ReadOptions& options, const LookupKey& k,
                    std::string* value, GetStats* stats) {
  stats->seek_file = nullptr;                                             // 初始化 stats seek_file为空 查找层级为-1
  stats->seek_file_level = -1;

  struct State {                                                          // 定义一个State结构体
    Saver saver;
    GetStats* stats;
    const ReadOptions* options;                                           // 设置选项
    Slice ikey;
    FileMetaData* last_file_read;                                         // 设置文件源信息
    int last_file_read_level;

    VersionSet* vset;
    Status s;
    bool found;

    static bool Match(void* arg, int level, FileMetaData* f) {            // 匹配方法
      State* state = reinterpret_cast<State*>(arg);

      if (state->stats->seek_file == nullptr &&
          state->last_file_read != nullptr) {
        // We have had more than one seek for this read.  Charge the 1st file.
        state->stats->seek_file = state->last_file_read;                        // 设置当前查找值
        state->stats->seek_file_level = state->last_file_read_level;
      }

      state->last_file_read = f;                                                // 设置当前元信息
      state->last_file_read_level = level;                                      // 设置当前层级

      state->s = state->vset->table_cache_->Get(*state->options, f->number,
                                                f->file_size, state->ikey,
                                                &state->saver, SaveValue);      // 查找具体数据 调用table_cache_的Get方法查找
      if (!state->s.ok()) {                                                     // 如果查找是否 设置已查找 返回False
        state->found = true;
        return false;
      }
      switch (state->saver.state) {                                             // 状态判断
        case kNotFound:
          return true;  // Keep searching in other files
        case kFound:
          state->found = true;                                                  // 如果找到则返回false
          return false;
        case kDeleted:
          return false;                                                         // 如果已删除也返回false
        case kCorrupt:
          state->s =
              Status::Corruption("corrupted key for ", state->saver.user_key);
          state->found = true;
          return false;
      }
    }
  };

  State state;                                                    // 初始化 state实例
  state.found = false;
  state.stats = stats;
  state.last_file_read = nullptr;
  state.last_file_read_level = -1;

  state.options = &options;
  state.ikey = k.internal_key();
  state.vset = vset_;

  state.saver.state = kNotFound;
  state.saver.ucmp = vset_->icmp_.user_comparator();
  state.saver.user_key = k.user_key();
  state.saver.value = value;

  ForEachOverlapping(state.saver.user_key, state.ikey, &state, &State::Match);      // 查找key

  return state.found ? state.s : Status::NotFound(Slice());
}

主要在该方法内部定义了一个State结构体,然后调用ForEachOverlapping方法去具体查找内容;

void Version::ForEachOverlapping(Slice user_key, Slice internal_key, void* arg,
                                 bool (*func)(void*, int, FileMetaData*)) {
  const Comparator* ucmp = vset_->icmp_.user_comparator();                    // 先获取比较方法

  // Search level-0 in order from newest to oldest.
  std::vector<FileMetaData*> tmp;                                             // 初始化 一个 列表
  tmp.reserve(files_[0].size());                                              // 设置为层级为第一层的大小
  for (uint32_t i = 0; i < files_[0].size(); i++) {                           // 遍历第一层
    FileMetaData* f = files_[0][i];                                           // 获取元文件信息
    if (ucmp->Compare(user_key, f->smallest.user_key()) >= 0 &&
        ucmp->Compare(user_key, f->largest.user_key()) <= 0) {                // 如果当前值比最小的要大 比最大的要小 则压入数据中
      tmp.push_back(f);
    }
  }
  if (!tmp.empty()) {                                                         // 如果当前查找的不为空
    std::sort(tmp.begin(), tmp.end(), NewestFirst);                           // 排序该列表 排序规则按照大小排序
    for (uint32_t i = 0; i < tmp.size(); i++) {
      if (!(*func)(arg, 0, tmp[i])) {                                         // 遍历当前列表 并执行回调函数 该回调函数就是State中的Match函数如果找到则返回
        return;
      }
    }
  }

  // Search other levels.
  for (int level = 1; level < config::kNumLevels; level++) {                  // 如果在第一层没有找到 开始从第二层开始查找
    size_t num_files = files_[level].size();                                  // 获取当前层级的大小
    if (num_files == 0) continue;                                             // 如果当前层级为空则循环下一个

    // Binary search to find earliest index whose largest key >= internal_key.
    uint32_t index = FindFile(vset_->icmp_, files_[level], internal_key);     // 查找文件 中是否包含该值
    if (index < num_files) {                                                  // 如果当前索引值小于 当前层级数
      FileMetaData* f = files_[level][index];                                 // 获取当前的元信息
      if (ucmp->Compare(user_key, f->smallest.user_key()) < 0) {              // 编辑是否小于该文件最小的值 如果比最小值要大
        // All of "f" is past any data for user_key
      } else {
        if (!(*func)(arg, level, f)) {                                        // 使用回调函数检查是否找到该值
          return;
        }
      }
    }
  }
}

通过该方法可知,首先查找第一层,如果第一层找到了就直接返回,如果第一层没有找到则继续往下层查找,默认最高层数是7,此时查找的过程中,都是先比较每一层的元文件信息,比较该数据是否在该元文件信息之间,如果是之间,则在调用传入的回调函数Match进行精准查找,在Match方法中主要的比较方法如下;

state->s = state->vset->table_cache_->Get(*state->options, f->number,
                                                f->file_size, state->ikey,
                                                &state->saver, SaveValue); 

调用了vset的table_cache_方法中的Get方法;

Status TableCache::Get(const ReadOptions& options, uint64_t file_number,
                       uint64_t file_size, const Slice& k, void* arg,
                       void (*handle_result)(void*, const Slice&,
                                             const Slice&)) {
  Cache::Handle* handle = nullptr;
  Status s = FindTable(file_number, file_size, &handle);                            // 查找文件
  if (s.ok()) {
    Table* t = reinterpret_cast<TableAndFile*>(cache_->Value(handle))->table;       // 如果找到 则获取table
    s = t->InternalGet(options, k, arg, handle_result);
    cache_->Release(handle);
  }
  return s;
}

其中传入了SaveValue的回调处理函数,来再次确认找到的值,并将值保存;此时首先调用FindTable方法去查找文件;

Status TableCache::FindTable(uint64_t file_number, uint64_t file_size,
                             Cache::Handle** handle) {
  Status s;
  char buf[sizeof(file_number)];                                          // 获取当前的文件大小
  EncodeFixed64(buf, file_number);
  Slice key(buf, sizeof(buf));
  *handle = cache_->Lookup(key);                                          // 调用cache_的Lookup方法来查找该key 使用了LRU算法
  if (*handle == nullptr) {                                               // 如果没有找到 则新生成
    std::string fname = TableFileName(dbname_, file_number);              // 新生成一个TableFileName 
    RandomAccessFile* file = nullptr;
    Table* table = nullptr;
    s = env_->NewRandomAccessFile(fname, &file);                          // 初始化 检查 该文件是否可用
    if (!s.ok()) {
      std::string old_fname = SSTTableFileName(dbname_, file_number);     // 生成一个SSTTableFileName文件实例
      if (env_->NewRandomAccessFile(old_fname, &file).ok()) {             // 检查是否成功
        s = Status::OK();
      }
    }
    if (s.ok()) {
      s = Table::Open(options_, file, file_size, &table);                 // 如果检查成功 则打开该文件
    }

    if (!s.ok()) {                                                        // ruguo 打开失败则 重置数据
      assert(table == nullptr);
      delete file;
      // We do not cache error results so that if the error is transient,
      // or somebody repairs the file, we recover automatically.
    } else {
      TableAndFile* tf = new TableAndFile;                                // 新生成一个TableFileName 
      tf->file = file;
      tf->table = table;
      *handle = cache_->Insert(key, tf, 1, &DeleteEntry);                 // 在缓存中插入该数据
    }
  }
  return s;
}

主要就是检查输入的文件是否可以打开,并检查模式是否可以,最后将生成的文件加入到缓存中,以便后续查找该值能够更快找到;

Status Table::InternalGet(const ReadOptions& options, const Slice& k, void* arg,
                          void (*handle_result)(void*, const Slice&,
                                                const Slice&)) {
  Status s;
  Iterator* iiter = rep_->index_block->NewIterator(rep_->options.comparator);       // 设置迭代器
  iiter->Seek(k);                                                                   // 查找该key 
  if (iiter->Valid()) {                                                             // 如果找到
    Slice handle_value = iiter->value();                                            // 设置该值
    FilterBlockReader* filter = rep_->filter;
    BlockHandle handle;
    if (filter != nullptr && handle.DecodeFrom(&handle_value).ok() &&
        !filter->KeyMayMatch(handle.offset(), k)) {                                 // 通过过滤器来检查该值是否合法
      // Not found
    } else {
      Iterator* block_iter = BlockReader(this, options, iiter->value());            // 设置一个迭代器    
      block_iter->Seek(k);                                                          // 查找该值
      if (block_iter->Valid()) {
        (*handle_result)(arg, block_iter->key(), block_iter->value());              // 调用回调函数处理
      }
      s = block_iter->status();                                                     // 获取状态
      delete block_iter;
    }
  }
  if (s.ok()) {
    s = iiter->status();
  }
  delete iiter;
  return s;
}

该方法主要就是查找值,通过多层次的iter迭代器的包装,主要是为了加入其它的如加入缓存,或者注册相关的处理事件,所以导致BlockReader和rep_->index_block->NewIterator多次检查了待查找的值;

Iterator* Table::BlockReader(void* arg, const ReadOptions& options,
                             const Slice& index_value) {
  Table* table = reinterpret_cast<Table*>(arg);                 // 获取table 
  Cache* block_cache = table->rep_->options.block_cache;        // 获取cache  
  Block* block = nullptr;
  Cache::Handle* cache_handle = nullptr;

  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;
    if (block_cache != nullptr) {
      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);                            // 查找该值
      if (cache_handle != nullptr) {
        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);                                          // 新生成一个block
          if (contents.cachable && options.fill_cache) {
            cache_handle = block_cache->Insert(key, block, block->size(),
                                               &DeleteCachedBlock);             // 在缓存中插入
          }
        }
      }
    } else {
      s = ReadBlock(table->rep_->file, options, handle, &contents);
      if (s.ok()) {
        block = new Block(contents);
      }
    }
  }

  Iterator* iter;                                                                 // 设置迭代器
  if (block != nullptr) {
    iter = block->NewIterator(table->rep_->options.comparator);                   // 生成一个默认的迭代器
    if (cache_handle == nullptr) {
      iter->RegisterCleanup(&DeleteBlock, block, nullptr);                        // 注册一个删除 列表 等到执行完成后删除
    } else {
      iter->RegisterCleanup(&ReleaseBlock, block_cache, cache_handle);
    }
  } else {
    iter = NewErrorIterator(s);
  }
  return iter;
}

至此,在层级文件中的查找流程基本完成,从流程可以看出在层级查找的过程中,机制更为复杂,设置了更多的缓存与检查机制。

总结

本文主要是讲述了leveldb数据的获取流程,当获取数据的时候可能会出现从当前内存中的memtable中获取或者是不可变immtable中获取,如果两者都获取不到,则去层级文件中去查找,在层级文件中查找,还需要确定查找的内容是属于哪一层级,然后通过添加缓存等方式,来提高读的性能,然后注册相应的回调机制来保证数据的流程的高效。由于本人才疏学浅,如有错误请批评指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值