Leveldb --- SkipList

什么是skiplist

skiplist是一种多级的排序链表,为什么要是多级的呢?
考虑一个链表如下:

1 —> 2—> 5—> 10—> 20—> 37

如果想要查询10,我们要从头查询4次,这就是O(n)的查询效率,这样的效率太低了,怎么办呢?我们可以按一定算法选出一些节点当作索引,比如:选择1、5、20作为一级索引,这样这个表就变成了:

1 ———-> 5———–> 20——->|
1 —> 2—> 5—> 10—> 20—> 37

我们再查询,就可以从上面的层级开始遍历,查询1、5,5的下一节点是20 已经大于10,所以要跳到下一层,还是从5开始,5的下一节点是10,找到了。
路径是1、5、10。当节点多且建立更多级合理的索引时,查询效率会达到O(lgn),与平衡树接近。最关键的是实现简单。
skiplist在redis和leveldb中都有应用。leveldb 的数据在内存中的格式是memtable,而memtable就是用skiplist实现的。

leveldb中的skiplist

template<typename Key, class Comparator>
class SkipList {
 private:
  struct Node;

 public:
  explicit SkipList(Comparator cmp, Arena* arena);

  // Insert key into the list.
  // REQUIRES: nothing that compares equal to key is currently in the list.
  void Insert(const Key& key);

  // Returns true iff an entry that compares equal to key is in the list.
  bool Contains(const Key& key) const;

  // Iteration over the contents of a skip list
...           //其余细节省略
  };

这个skiplist的接口很简单,也可以发现这个skiplist没有del或者update,只有insert,这是因为,leveledb中的删除只是insert(key, deleteflag),当memtable到达一定容量时,会变成immutable(只读) memtable,再生成一个memtable,后台线程会将immutable memtable dump成 sstable(磁盘文件格式),然后再compation sstable,这时会删掉无用的数据。所以这个skiplist也不需要del接口。下面来看 insert接口,

template<typename Key, class Comparator>
void SkipList<Key,Comparator>::Insert(const Key& key) {

  Node* prev[kMaxHeight];    //记录遍历的路径
  Node* x = FindGreaterOrEqual(key, pref); //查找位置

  // Our data structure does not allow duplicate insertion
  assert(x == NULL || !Equal(key, x->key));//不允许重复数据

  int height = RandomHeight(); //在skiplist中生成的随机高度
  if (height > GetMaxHeight()) {  //如果生成的高度大于定义好的最大高度
    for (int i = GetMaxHeight(); i < height; i++) {
      prev[i] = head_;    //补充路径
    }
  max_height_.NoBarrier_Store(reinterpret_cast<void*>(height));
  }

  x = NewNode(key, height);//为当前的key 生成新的node
  for (int i = 0; i < height; i++) { //从最低级 到随机高度 插入新列,最低层的数据最全,然后级越高,数据越少。所以如果高层级有此节点,则低层级也一定有。
    // NoBarrier_SetNext() suffices since we will add a barrier when
    // we publish a pointer to "x" in prev[i].
    //这两行代码的效果是这样 原数据是   1--> 3  现在插入2
    //则先 2-->3  ,然后再 1-->2。简单的链表插入
    x->NoBarrier_SetNext(i, prev[i]->NoBarrier_Next(i)); 
    prev[i]->SetNext(i, x);
  }
}

从最高层级往下遍历。

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大于当前节点
      // Keep searching in this list
      x = next;
    } else { //小于
      if (prev != NULL) prev[level] = x;//记录
      if (level == 0) {
        return next; //找到了位置
      } else {
        // Switch to next list
        level--;// 向下一级查找
      }
    }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值