Memtable 
leveldb数据写入时并非直接落盘,而是先保存在内存中,在内存中的数据按key进行排序。当内存中的数据达到一定大小时,再将这批数据批量写入磁盘。在内存中的数据结构我们称之为Memtable,本节将介绍Memtable的实现。  先看代码:  
class MemTable {
 public:
  
  
  explicit MemTable(const InternalKeyComparator& comparator);
  MemTable(const MemTable&) = delete;
  MemTable& operator=(const MemTable&) = delete;
  
  void Ref() { ++refs_; }
  
  void Unref() {
    --refs_;
    assert(refs_ >= 0);
    if (refs_ <= 0) {
      delete this;
    }
  }
  
  
  size_t ApproximateMemoryUsage();
  
  
  
  
  
  
  Iterator* NewIterator();
  
  
  
  void Add(SequenceNumber seq, ValueType type, const Slice& key,
           const Slice& value);
  
  
  
  
  bool Get(const LookupKey& key, std::string* value, Status* s);
 private:
  friend class MemTableIterator;
  friend class MemTableBackwardIterator;
  struct KeyComparator {
    const InternalKeyComparator comparator;
    explicit KeyComparator(const InternalKeyComparator& c) : comparator(c) {}
    int operator()(const char* a, const char* b) const;
  };
  typedef SkipList<const char*, KeyComparator> Table;
  ~MemTable();  
  KeyComparator comparator_;
  int refs_;
  Arena arena_;
  Table table_;
};
  
上述代码中Add和Get函数即为向Memtable中写入和读取数据接口。  void Add(SequenceNumber seq, ValueType type, const Slice& key,  const Slice& value);第一个参数seq即全局唯一的序号,每进行一次更新操作序号加1,第二个参数type是表示该操作的类型:包括PUT和DELETE两种,key表示要添加或删除的key,value表示要添加的key对应的value(当type为DELETE时value为空)。  bool Get(const LookupKey& key, std::string* value, Status* s);当Memtable中存在所查找的key时,value存储其值,函数返回true;当Memtable中存在所查找key的删除标记时,s中存储NotFound的状态值,函数返回true;其他情况,函数返回false。  Memtable具体如何实现呢?主要在它的成员变量Table table_,即SkipList。  
class SkipList {
 private:
  struct Node;
 public:
  
  
  
  explicit SkipList(Comparator cmp, Arena* arena);
  SkipList(const SkipList&) = delete;
  SkipList& operator=(const SkipList&) = delete;
  
  
  void Insert(const Key& key);
  
  bool Contains(const Key& key) const;
  
  class Iterator {
   public:
    
    
    explicit Iterator(const SkipList* list);
    
    bool Valid() const;
    
    
    const Key& key() const;
    
    
    void Next();
    
    
    void Prev();
    
    void Seek(const Key& target);
    
    
    void SeekToFirst();
    
    
    void SeekToLast();
   private:
    const SkipList* list_;
    Node* node_;
    
  };
 private:
  enum { kMaxHeight = 12 };
  inline int GetMaxHeight() const {
    return max_height_.load(std::memory_order_relaxed);
  }
  Node* NewNode(const Key& key, int height);
  int RandomHeight();
  bool Equal(const Key& a, const Key& b) const { return (compare_(a, b) == 0); }
  
  bool KeyIsAfterNode(const Key& key, Node* n) const;
  
  
  
  
  
  Node* FindGreaterOrEqual(const Key& key, Node** prev) const;
  
  
  Node* FindLessThan(const Key& key) const;
  
  
  Node* FindLast() const;
  
  Comparator const compare_;
  Arena* const arena_;  
  Node* const head_;
  
  
  std::atomic<int> max_height_;  
  
  Random rnd_;
};
  
Skiplist类提供了两个主要方法,Insert和Contains。前者即向Skiplist中插入一个key,后者则是判断Skiplist中是否存在这个key。当然还提供了一个Iterator类,用于迭代Skiplist中的所有key值。  
Skiplist 
接下来介绍一下Skiplist:  Skiplist故名思义,即跳跃的链表。传统的链表我们要查找时只能依次遍历,效率很低;而skiplist在查找时,则可以跳跃的方式遍历,从而提高了查询效率。其原理是怎么样的呢?  跳表是由William Pugh发明。他在 Communications of the ACM June 1990, 33(6) 668-676 发表了Skip lists: a probabilistic alternative to balanced trees,在该论文中详细解释了跳表的数据结构和插入删除操作。  跳表是平衡树的一种替代的数据结构,但是和红黑树不相同的是,跳表对于树的平衡的实现是基于一种随机化的算法的,这样也就是说跳表的插入和删除的工作是比较简单的。  
下面来研究一下跳表的核心思想:  
先从链表开始,如果是一个简单的链表,那么我们知道在链表中查找一个元素I的话,需要将整个链表遍历一次。  
   
如果是说链表是排序的,并且节点中还存储了指向前面第二个节点的指针的话,那么在查找一个节点时,仅仅需要遍历N/2个节点即可。  
   
这基本上就是跳表的核心思想,其实也是一种通过“空间来换取时间”的一个算法,通过在每个节点中增加了向前的指针,从而提升查找的效率。而leveldb的Memtable即使用了基于该思想的skiplist。 
                
                
                
        
        
    
  
 
 |