RocksDB源码学习(三):读(二)

本篇博客关注 RocksDB 从 immutable memtable 中的读取,所有代码版本均为 v7.7.3

在 Rocksdb 中,memtable 和 immutable memtable 都位于内存,且它俩是一样的数据结构,唯一的区别是memtable 可读可写,而 immutable memtable 是只读的,不允许写入。写入从 writebatch 提交后,会进入 memtable,一旦一个 memtable 写满了,那么它就会转化成一个 immutable memtable,作为向 sstable 转换的过度态,然后生成一个新的空 memtable。

Rocksdb 引入了 CF 的概念,在一个 CF 中只有一个 memtable,但允许存在多个 immutable memtable。当从 memtable 中没读到时,就会尝试在这些 immutable memtable 中读。


从上一篇博客,读操作从 DBImpl::GetImpl() 开始依次从不同的部分读取,所以这里的分析如果依旧是 DBImpl::GetImpl()。截取部分代码,如下:

Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
                       GetImplOptions& get_impl_options) {
  // ....
    if (get_impl_options.get_value) {
      if (sv->mem->Get(
              lkey,
              get_impl_options.value ? get_impl_options.value->GetSelf()
                                     : nullptr,
              get_impl_options.columns, timestamp, &s, &merge_context,
              &max_covering_tombstone_seq, read_options,
              false /* immutable_memtable */, get_impl_options.callback,
              get_impl_options.is_blob_index)) {
        done = true;

        if (get_impl_options.value) {
          get_impl_options.value->PinSelf();
        }

        RecordTick(stats_, MEMTABLE_HIT);
      } else if ((s.ok() || s.IsMergeInProgress()) &&
                 sv->imm->Get(lkey,
                              get_impl_options.value
                                  ? get_impl_options.value->GetSelf()
                                  : nullptr,
                              get_impl_options.columns, timestamp, &s,
                              &merge_context, &max_covering_tombstone_seq,
                              read_options, get_impl_options.callback,
                              get_impl_options.is_blob_index)) {
        done = true;

        if (get_impl_options.value) {
          get_impl_options.value->PinSelf();
        }

        RecordTick(stats_, MEMTABLE_HIT);
      }
    }
 // ...
}

代码的前半部分就是上一篇博客的入口,即 MemTable::Get()。而如果没有找到,则会调用 sv->imm->Get(),开始从 immutable memtable 中读取。实际上,这个 imm 并不是一个 immutable memtable,而是多个 immutable memtable 组成的一个链表,因为 RocksDB 中一个 CF 允许多个 immutable memetable 存在。

MemTableListVersion* imm;

类结构如下:

class MemTableListVersion {
 // ...
 private:
  // Immutable MemTables that have not yet been flushed.
  std::list<MemTable*> memlist_;
  // ...
}

可以看出来,immutable memtable 和 memtable 是一个数据结构,所以 imm 可以被看为一个由只读 memtable 组成的链表。

sv->imm->Get() 实际上调用的是 MemTableListVersion::Get(),其完整代码如下:

// Search all the memtables starting from the most recent one.
// Return the most recent value found, if any.
// Operands stores the list of merge operations to apply, so far.
bool MemTableListVersion::Get(const LookupKey& key, std::string* value,
                              PinnableWideColumns* columns,
                              std::string* timestamp, Status* s,
                              MergeContext* merge_context,
                              SequenceNumber* max_covering_tombstone_seq,
                              SequenceNumber* seq, const ReadOptions& read_opts,
                              ReadCallback* callback, bool* is_blob_index) {
  return GetFromList(&memlist_, key, value, columns, timestamp, s,
                     merge_context, max_covering_tombstone_seq, seq, read_opts,
                     callback, is_blob_index);
}

GetFromList() 的第一个参数为 memlist_,也就是 immutable memtable 组成的链表。该函数完整源码如下:

bool MemTableListVersion::GetFromList(
    std::list<MemTable*>* list, const LookupKey& key, std::string* value,
    PinnableWideColumns* columns, std::string* timestamp, Status* s,
    MergeContext* merge_context, SequenceNumber* max_covering_tombstone_seq,
    SequenceNumber* seq, const ReadOptions& read_opts, ReadCallback* callback,
    bool* is_blob_index) {
  *seq = kMaxSequenceNumber;

  for (auto& memtable : *list) {
    assert(memtable->IsFragmentedRangeTombstonesConstructed());
    SequenceNumber current_seq = kMaxSequenceNumber;

    bool done =
        memtable->Get(key, value, columns, timestamp, s, merge_context,
                      max_covering_tombstone_seq, &current_seq, read_opts,
                      true /* immutable_memtable */, callback, is_blob_index);
    if (*seq == kMaxSequenceNumber) {
      // Store the most recent sequence number of any operation on this key.
      // Since we only care about the most recent change, we only need to
      // return the first operation found when searching memtables in
      // reverse-chronological order.
      // current_seq would be equal to kMaxSequenceNumber if the value was to be
      // skipped. This allows seq to be assigned again when the next value is
      // read.
      *seq = current_seq;
    }

    if (done) {
      assert(*seq != kMaxSequenceNumber || s->IsNotFound());
      return true;
    }
    if (!done && !s->ok() && !s->IsMergeInProgress() && !s->IsNotFound()) {
      return false;
    }
  }
  return false;
}

可以看到,该函数就是遍历链表,从每一个 immutable memtable 中调用 MemTable::Get(),一旦读取成功就返回。MemTable::Get() 就是上一篇博客分析的从 memtable 中读取,因此从 immutable memtable 中读和从 memtable 中读完全一致。


至此,在内存中的读取操作已经分析完毕,下一篇博客将分析从磁盘 sstable 中的读取。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据Valgrind提供的信息,可以得出以下分析: 这段Valgrind信息表示在程序运行结束时,有24字节的内存块是明确丢失的。这是在294条记录中的第68条记录。 这个内存块的分配是通过`operator new`函数进行的,具体是在`vg_replace_malloc.c`文件的`operator new(unsigned long, std::nothrow_t const&)`函数中进行的。这个函数用于分配内存,并且使用了`std::nothrow_t`参数,表示在分配失败时不抛出异常。 这个内存块的丢失发生在`libstdc++.so.6.0.19`库文件中的`__cxa_thread_atexit`函数中。这个函数是C++标准库中的一个线程退出钩子函数,用于在线程退出时执行清理操作。 进一步跟踪,这个内存块的丢失是在`librocksdb.so.6.20.3`库文件中的`rocksdb::InstrumentedMutex::Lock()`函数中发生的。这个函数是RocksDB数据库引擎的一个锁操作函数,用于获取互斥锁。 在调用堆栈中,可以看到这个内存块丢失是在RocksDB数据库引擎的后台合并线程(Background Compaction)中发生的。具体是在`rocksdb::DBImpl::BackgroundCallCompaction()`和`rocksdb::DBImpl::BGWorkCompaction()`函数中进行的合并操作。 最后,从调用堆栈中可以看到,这个内存块的丢失是在后台线程中发生的。这是在`librocksdb.so.6.20.3`库文件中的`rocksdb::ThreadPoolImpl::Impl::BGThread()`和`rocksdb::ThreadPoolImpl::Impl::BGThreadWrapper()`函数中执行的。 综上所述,根据Valgrind的信息分析,这段代码中存在一个明确的内存泄漏问题,24字节的内存块在后台合并线程中丢失。需要进一步检查代码,确保在合适的时机释放这些内存块,以避免资源泄漏和潜在的问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值