leveldb源码分析--Memtable

本节讲述内存中LevelDB的数据结构Memtable,Memtable义如其名即为内存中的KV Table,即LSM-Tree中的C0 Tree。我们知道在LSM-Tree中刚插入的的KV数据都是存储在内存中,当内存中存储的数据超过一定量以后再写到磁盘中。而对于leveldb来说这个过程演变为内存中的数据都是插入到MemTable中,当MemTable中的数据超过一定量(Options.write_buffer_size)以后MemTable就转化为Immutable Memtable等待dump到磁盘中的SSTable中。Immutable Memtable从结构上讲和Memtable是完全一样的,区别仅仅在于其是只读的,不允许写入操作,而Memtable则是允许写入和读取的,而当MemTable转化为Immutable MemTable时会生成一个MemTable。

为了理解MemTable,我们首先看一下他包含了那些成员变量

typedef SkipList<const char*, KeyComparator> Table;

  KeyComparator comparator_;  //比较器,用来判断Key的顺序,稍后介绍
  int refs_;                  //引用计数,当引用为0时作一个自我销毁
  Arena arena_;               //简易的内存池
  Table table_;               //保存KV对的实际结构,其为一个SkipList

上文提到了当内存中的数据超过一定的量时,MemTable就会转化为Immutable Table,这个量的管理和判断就是通过Arena来实现的。它的工作十分简单,将申请到的内存块放入std::vector blocks_中,在Arena的生命周期结束后,统一释放掉所有申请到的内存,内部结构如图所示:

只是在处理时涉及到了一个小技巧,Arena会先预先分配一块4K大小的内存,如果后继有内存请求就从其中进行获取,如果剩下的空间不够请求就又两种处理方式:1. 申请大小 > 1K,那么直接申请一块新的对应的大小并存入vector中;2. 申请大小 <= 1K, 申请一块4K的内存,然后在其中分配相应大小的空间给请求者。

Arena主要提供了两个申请函数:其中一个直接分配内存,另一个可以申请对齐的内存空间。这种设计应该说这是和leveldb特定的应用场景相关的,比如一个memtable使用一个Arena,当memtable被释放时,由Arena统一释放其内存。而小内存预先分配的方式可以有效减少内存申请的系统调用的次数,降低对应的开销。理解了流程,我们还是看看代码吧:

char* alloc_ptr_;            //当前预分配的4K的地址指针
   size_t alloc_bytes_remaining_; //预分配内存尚未使用的大小
   std::vector<char*> blocks_;     //已经分配的内存地址指针集合
   size_t blocks_memory_;          //已经分配的内存大小

申请流程,只列出申请不不对齐的方法,申请对齐的内存只是加了一个内存对齐的过程,逻辑很简单

inline char* Arena::Allocate(size_t bytes) {
  if (bytes <= alloc_bytes_remaining_) {
    //剩余内存大于申请,直接从预分配的内存中取走相应大小
    char* result = alloc_ptr_;
    alloc_ptr_ += bytes;
    alloc_bytes_remaining_ -= bytes;
    return result;
  }
  return AllocateFallback(bytes);
}
char* Arena::AllocateFallback(size_t bytes) {
  //if > 1K 直接分配一块内存然后返回给申请者
  if (bytes > kBlockSize / 4) {
    char* result = AllocateNewBlock(bytes);
    return result;
  }

  // 否则申请一块 4K 的内存然后取相应的大小返回给请求者.
  alloc_ptr_ = AllocateNewBlock(kBlockSize);
  alloc_bytes_remaining_ = kBlockSize;

  char* result = alloc_ptr_;
  alloc_ptr_ += bytes;
  alloc_bytes_remaining_ -= bytes;
  return result;
}

这里Arena的使用原理就分析完成了,在其析构的时候会释放掉所有的内存。

至于SkipList leveldb也就是对齐有一个具体的实现,对齐详细的介绍和说明有一篇文章《SkipList 跳表》有详细的介绍。接下来的文章我们介绍一下MemTable的迭代器和Comparator。

leveldb源码分析--Memtable,布布扣,bubuko.com

时间: 2024-12-21 12:18:18

leveldb源码分析--Memtable的相关文章

leveldb源码分析--SSTable之TableBuilder

上一篇文章讲述了SSTable的格式以后,本文结合源码解析SSTable是如何生成的. void TableBuilder::Add(const Slice& key, const Slice& value) { //如果已经插入过数据,那么要保证当前插入的key > 之前最后一次插入的key, // SSTable必须是有序的插入数据 if (r->num_entries > 0) { assert(r->options.comparator->Compar

LevelDB源码分析--Cache及Get查找流程

本打算接下来分析version相关的概念,但是在准备的过程中看到了VersionSet的table_cache_这个变量才想起还有这样一个模块尚未分析,经过权衡觉得leveldb的version相对Cache来说相对复杂,而且version虽然对整个leveldb来说实现上跟其他功能十分紧密,但是从概念上来说却相对弱很多,有点感觉是附加的功能的感觉.所以从介绍系统首先应该注意的是整个系统概念的完整性的角度说还是先分析Cache相关的功能. 我们先来看Cache的基本框架结构数据: struct

leveldb源码分析--SSTable之Compaction

对于compaction是leveldb中体量最大的一部分,也应该是最为复杂的部分,为了便于理解我们首先从一些基本的概念开始.下面是一些从doc/impl.html中翻译和整理的内容: Level 0 当日志文件超过一定大小的阈值是 (默认为 1MB): 建立一个新的memtable和日志文件,以后的操作都是用新的memtable和日志文件 后台进行如下操作: 将旧的 memtable写到SSTable中(过程为先转为immtable_table,然后遍历写入) 废弃旧的 memtable 删除

leveldb源码分析--插入删除流程

由于网络上对leveldb的分析文章都比较丰富,一些基础概念和模型都介绍得比较多,所以本人就不再对这些概念以专门的篇幅进行介绍,本文主要以代码流程注释的方式. 首先我们从db的插入和删除开始以对整个体系有一个感性的认识,首先看插入: Status DB::Put(const WriteOptions& opt, const Slice& key, const Slice& value) { WriteBatch batch; //leveldb中不管单个插入还是多个插入都是以Wri

leveldb源码分析--Key结构

[注]本文参考了sparkliang的专栏的Leveldb源码分析--3并进行了一定的重组和排版 经过上一篇文章的分析我们队leveldb的插入流程有了一定的认识,而该文设计最多的又是Batch的概念.这篇文章本来应该顺理成章的介绍Batch相关流程和结构了,但是Batch涉及到了一些编码和Key相关的概念,所以我觉得应该先理清这方面的概念有助于大家更容易理解后面的内容. 在dbformat.h/cc文件中我们首先看到的是 typedef uint64_t SequenceNumber; str

leveldb源码分析--WriteBatch

从[leveldb源码分析--插入删除流程]和WriteBatch其名我们就很轻易的知道,这个是leveldb内部的一个批量写的结构,在leveldb为了提高插入和删除的效率,在其插入过程中都采用了批量集合相邻的多个具有相同同步设置的写请求以批量的方式进行写入. 其成员变量仅包含了一个  std::string 类型的 rep_变量,其Put和Delete(其实也是插入删除操作,而非删除Put进去的数据,或者你可以将其理解为Put Delete operation的过度简写)都将相应的操作Enc

leveldb源码分析--Comparator

既然leveldb是一个按Key序组织的LSM-Tree实现,那么对于Key的比较就是非常之重要了,这个Key的比较在leveldb中是Comparator的形式出现的.我们首先来看看Comparator的基本方法有哪些 // 实际的比较函数 virtual int Compare(const Slice& a, const Slice& b) const = 0; // 名称,主要是为了防止建立和读取时使用了不同的Comparator virtual const char* Name()

leveldb源码分析—Recover和Repair

leveldb作为一个KV存储引擎将数据持久化到磁盘,而对于一个存储引擎来说在存储过程中因为一些其他原因导致程序down掉甚至数据文件被破坏等都会导致程序不能按正常流程再次启动.那么遇到这些状况以后如何使程序最大程度的恢复数据就是非常重要的一项工作,leveldb也提供了这方面的工作. 首先来看recover,这是每一次启动数据库的时候都会呗调用到的流程.其功能是恢复数据库在运行中突然因为某些原因down掉而这个时候leveldb中的丢失的当前状态,以及memtable甚至immtable中还未

LevelDB源码分析--Iterator

我们先来参考来至使用Iterator简化代码2-TwoLevelIterator的例子,略微修改希望能帮助更加容易立即,如果有不理解请各位看客阅读原文. 下面我们再来看一个例子,我们为一个书店写程序,书店里有许多书Book,每个书架(BookShelf)上有多本书. 类结构如下所示 class Book { private: string book_name_; }; class Shelf { private: vector<Book> books_; }; 如何遍历书架上所有的书呢?一种实