leveldb源码学习——db读写流程

本文详细介绍了leveldb的读写流程。在读流程中,数据按memtable、imm和sst文件顺序查找,使用version确保一致性。Get操作会触发compaction,当对 SST 文件的seek次数过多时。而在写流程中,数据写入涉及队列和writer结构,确保并发安全性,并通过背景线程执行compaction。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

读流程

当用户调用Get方法从leveldb中读取一个key的value时,leveldb按照memtable,imm,sst文件的顺序,依次寻找key。其中,在sst文件中搜索是通过在当前version中Get来实现的,version是sst文件的一个snapshot,可以保证版本一致,如果没有version机制,在get过程中,此key所在sst文件被合并到了其他文件中,则可能会get失败。

关键代码

Status DBImpl::Get(const ReadOptions& options,
                   const Slice& key,
                   std::string* value) {
  Status s;
  MutexLock l(&mutex_);
  SequenceNumber snapshot;
  if (options.snapshot != NULL) {
    snapshot = reinterpret_cast<const SnapshotImpl*>(options.snapshot)->number_;
  } else {
    snapshot = versions_->LastSequence();
  }

  MemTable* mem = mem_;
  MemTable* imm = imm_;
  Version* current = versions_->current();
  mem->Ref();
  if (imm != NULL) imm->Ref();
  current->Ref();

  bool have_stat_update = 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);
    //依次从mem,imm,current version中get对应的value
    if (mem->Get(lkey, value, &s)) {
      // Done
    } else if (imm != NULL && imm->Get(lkey, value, &s)) {
      // Done
    } else {
      s = current->Get(options, lkey, value, &stats);
      have_stat_update = true;
    }
    mutex_.Lock();
  }

  if (have_stat_update && current->UpdateStats(stats)) {
    MaybeScheduleCompaction();
  }
  mem->Unref();
  if (imm != NULL) imm->Unref();
  current->Unref();
  return s;
}

需要注意的点

  • 用户通过user-key来查询,在leveldb中首先会通过user-key和sequence num组成lookupkey,在三处查询,均使用lookupkey

  • Get时会首先获取锁,在临界区获取mem,imm,current version,当前seq等信息,而从三处查询时,不需要持有锁,因为对以上三处是只读

  • Get也可能会触发compaction,每个get会导致对sst文件seek,leveldb会记录对每个sst文件的seek次数,到达某个值之后,会将此文件compaction,其中的道理是,假设用户查询key是随机的,那文件越大,其被seek的次数应该越多,leveldb假设16k对应一次查询,比如文件大小为160k,那么它最多被seek10次,seek次数超过了10,就说明它的range太大了,应该被compaction(存疑?)

  • 对sst文件的搜索,其实是按照level0,level1…的顺序进行的,但是这些细节都被封装在version->Get函数中,查看version->Get函数可以看到,首先根据key找到对应的file meta信息(version中保存了各个level的file meta信息),然后根据file meta信息(file number)到cache中找获取对应的TableAndFile(如果没有,则RandomAccess方式打开对应的文件),然后通过Table中的index block,filter block等信息来具体定位key在文件中的位置,期间,可能使用block cache。

  • MaybeSchedualCompaction函数会将一次compaction的判断和compaction放到后台任务队列中,后台会有一个bg线程消费此队列

写流程

大致上, 写入一条k-v的流程为

  • 将k-v包装成为一个writer结构
  • 放到一个writers 队列中,等待,直到被唤醒(自己的任务被执行或者自己的任务在队列头)
  • 如果线程发现自己的任务在队列头,则将队列中目前所有的writer组合起来,先写log,再写入mem
  • 如果线程发现自己的任务已经被执行,则返回

类比如下场景
一群人要去一个窗口交表, 先排队,如果发现自己不在队首,就睡觉,如果自己在队首,就把当前排队的人群中所有的表都收上来,代为递交,然后再挨个通知排队的人他的表已经交上去了,可以回家了。

需要注意的是

  • 系统中任意时刻, 只有一个线程在执行写mem的操作,所以写入mem不需要加锁
  • 每个线程都可能成为执行写入mem的线程

还没有想通的问题
为什么这样会提高效率?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值