LevelDB 源码分析(七):Skiplist

栏目: 数据库 · 发布时间: 6年前

内容简介:LevelDB 源码分析(七):Skiplist

LevelDB 中的 memtable 只是一个封装类,它的底层实现是一个跳表。跳表是一种基于随机数的平衡数据结构,其他的平衡数据结构还有红黑树、AVL树,但跳表的原理比它们简单很多。跳表有点像链表,只不过每个节点是多层结构,通过在每个节点中增加向前的指针提高查找效率。

/leveldb/db 文件夹下有跳表的实现 skiplist.h 和跳表的测试程序 skiplist_test.cc

LevelDB 的 skiplist 是一个模板类,key 是跳表每个节点存储的信息类,跳表是一个顺序结构,comparator 是跳表 key 的比较器。

Skiplist 的写操作需要外部的同步信号,最有可能就是利用 mutex。读操作需要保证在读的过程中skiplist不会被销毁。除此之外,读操作不需要任何的内部锁或者同步信号。

分配的节点直到 skiplist 销毁的时候才会 delete 掉,是通过代码层面来保证这一点的,因为代码中没有 delete skiplist 节点的操作。

当一个节点连接到 skiplist 上后,除了 prev 和 next,节点上的其他内容都不能改变了。

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

public:
// 创建一个skiplist。cmp是比较函数。利用arena分配内存
explicit SkipList(Comparator cmp, Arena* arena);

// 插入新值到skiplist中
// REQUIRES: nothing that compares equal to key is currently in the list.
void Insert(const Key& key);

// 判断key是否存在
bool Contains(const Key& key) const;

// skiplist的迭代器
class Iterator {
public:
// 在特定的skiplist上初始化一个迭代器
// The returned iterator is not valid.
explicit Iterator(const SkipList* list);

// 迭代器是否定位在一个有效的节点上
bool Valid() const;

// 返回当前位置的key
const Key& key() const;

// 进行到下一个位置
void Next();

// 进行到前一个位置
void Prev();

// 进行到 key >= target的第一个位置
void Seek(const Key& target);

// 定位到头节点
void SeekToFirst();

// 定位到尾节点
void SeekToLast();

private:
const SkipList* list_;
Node* node_;
// Intentionally copyable
};
private:
enum { kMaxHeight = 12 }; // skiplist的最大层数,新增节点的随机层数不能大于此值

Comparator const compare_; // key值的比较函数,一旦初始化就不能变化了(当插入一些数据后,改变key,状态不可控)
Arena* const arena_; // Arena 用来给结点分配内存

Node* const head_; // skiplist的头结点

// 只会被 Insert() 修改。Read racily by readers, but stale values are ok.
// AtomicPointer是leveldb定义的一个原子指针,
// 它的读取和写入都设立了内存屏障,保证读取的值是即时的、最新的。
// 这里直接将int型转化为指针保存,因为不会对其取地址。
port::AtomicPointer max_height_; // 当前skiplist的最大层数

// 返回Skiplist的层数
inline int GetMaxHeight() const {
return static_cast<int>(reinterpret_cast<intptr_t>(max_height_.NoBarrier_Load()));
}

// 产生随机的level层数,只有执行Insert()时才会被调用
Random rnd_;

Node* NewNode(const Key& key, int height);// 新建一个level=height,值为key的节点
int RandomHeight(); // 随机产生一个level层数

// 比较两个key是否相等
bool Equal(const Key& a, const Key& b) const { return (compare_(a, b) == 0); }

// key值是否比节点n的key值大
bool KeyIsAfterNode(const Key& key, Node* n) const;

// If prev is non-NULL, fills prev[level] with pointer to previous node at "level" for every level in [0..max_height_-1].
// 找到key对应的Node或是key后面紧邻的Node
Node* FindGreaterOrEqual(const Key& key, Node** prev) const;

// 找到key前面紧邻的Node,如果skiplist为空,则返回头节点
Node* FindLessThan(const Key& key) const;

// Skiplist最后一个Node,如果skiplist为空,则返回头节点
Node* FindLast() const;

// 拷贝构造和赋值构造操作不允许
SkipList(const SkipList&);
void operator=(const SkipList&);
};

具体的实现如下:

template<typename Key, class Comparator>
struct SkipList<Key,Comparator>::Node { // Skiplist节点Node定义
explicit Node(const Key& k) : key(k) { }

Key const key;

// Accessors/mutators for links. Wrapped in methods so we can
// add the appropriate barriers as necessary.
// 返回本节点第n层的下一个节点指针
Node* Next(int n) {
assert(n >= 0);
// Use an 'acquire load' so that we observe a fully initialized
// version of the returned Node.
return reinterpret_cast<Node*>(next_[n].Acquire_Load());
}
// 重设n层的next指针
void SetNext(int n, Node* x) {
assert(n >= 0);
// Use a 'release store' so that anybody who reads through this pointer observes a fully initialized version of the inserted node.
next_[n].Release_Store(x);
}

// No-barrier variants that can be safely used in a few locations.
Node* NoBarrier_Next(int n) {
assert(n >= 0);
return reinterpret_cast<Node*>(next_[n].NoBarrier_Load());
}
void NoBarrier_SetNext(int n, Node* x) {
assert(n >= 0);
next_[n].NoBarrier_Store(x);
}

private:
// 数组的长度等于节点的层数。next_[0] 表示最低的一层。
port::AtomicPointer next_[1];
};

//建一个Node节点(指定key及level层数)
template<typename Key, class Comparator>
typename SkipList<Key,Comparator>::Node* SkipList<Key,Comparator>::NewNode(const Key& key, int height) {
char* mem = arena_->AllocateAligned(sizeof(Node) + sizeof(port::AtomicPointer) * (height - 1));
return new (mem) Node(key);
}

template<typename Key, class Comparator>
inline SkipList<Key,Comparator>::Iterator::Iterator(const SkipList* list) {
list_ = list;
node_ = NULL;
}

template<typename Key, class Comparator>
inline bool SkipList<Key,Comparator>::Iterator::Valid() const {
return node_ != NULL;
}

template<typename Key, class Comparator>
inline const Key& SkipList<Key,Comparator>::Iterator::key() const {
assert(Valid());
return node_->key;
}

template<typename Key, class Comparator>
inline void SkipList<Key,Comparator>::Iterator::Next() {
assert(Valid());
node_ = node_->Next(0);
}

template<typename Key, class Comparator>
inline void SkipList<Key,Comparator>::Iterator::Prev() {
// 不需要使用 prev 链接,只要寻找 key 的前一个节点就行了
assert(Valid());
node_ = list_->FindLessThan(node_->key);
if (node_ == list_->head_) {
node_ = NULL;
}
}

template<typename Key, class Comparator>
inline void SkipList<Key,Comparator>::Iterator::Seek(const Key& target) {
node_ = list_->FindGreaterOrEqual(target, NULL);
}

template<typename Key, class Comparator>
inline void SkipList<Key,Comparator>::Iterator::SeekToFirst() {
node_ = list_->head_->Next(0);
}

template<typename Key, class Comparator>
inline void SkipList<Key,Comparator>::Iterator::SeekToLast() {
node_ = list_->FindLast();
if (node_ == list_->head_) {
node_ = NULL;
}
}

// 返回一个随机高度
template<typename Key, class Comparator>
int SkipList<Key,Comparator>::RandomHeight() {
// Increase height with probability 1 in kBranching
static const unsigned int kBranching = 4;
int height = 1;
while (height < kMaxHeight && ((rnd_.Next() % kBranching) == 0)) {
height++;
}
assert(height > 0);
assert(height <= kMaxHeight);
return height;
}

// key值是否比节点n的值大。如果节点n是NULL,则值表示无穷大
template<typename Key, class Comparator>
bool SkipList<Key,Comparator>::KeyIsAfterNode(const Key& key, Node* n) const {
return (n != NULL) && (compare_(n->key, key) < 0);
}

// 函数返回的前向节点指针数组是为了向跳表插入节点时用的
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; // 首先获得跳表的最高层数,减一是数组next最大下标
while (true) {
Node* next = x->Next(level);
if (KeyIsAfterNode(key, next)) { // 如果key在本节点node之后,继续前进
x = next; // 继续找
} else {
// 如果小于本节点node,把本节点的level层上的前节点指针记录进数组prev中,并跳向第一层的链表,
// 重复上述过程,直至来到最底层
if (prev != NULL)
prev[level] = x; // 当前level上,x为高度>=key节点高度,且正好排在其前面,插入和删除时使用
if (level == 0) {
return next;
} else {
level--;
}
}
}
}

template<typename Key, class Comparator>
typename SkipList<Key,Comparator>::Node*
SkipList<Key,Comparator>::FindLessThan(const Key& key) const {
Node* x = head_;
int level = GetMaxHeight() - 1;
while (true) {
assert(x == head_ || compare_(x->key, key) < 0);
Node* next = x->Next(level);
if (next == NULL || compare_(next->key, key) >= 0) { // 从最高level尽可能向后移动更远的距离
if (level == 0) { // 后面key>查找的key时,或next为空时,level--,直到level=0
return x;
} else {
level--; // 从最高层往下后续查找
}
} else {
x = next;
}
}
}

template<typename Key, class Comparator>
typename SkipList<Key,Comparator>::Node* SkipList<Key,Comparator>::FindLast()const {
//从最高level走到头,然后减少level继续走到头,一直到level=0
Node* x = head_;
int level = GetMaxHeight() - 1;
while (true) {
Node* next = x->Next(level);
if (next == NULL) {
if (level == 0) {
return x;
} else {
level--;
}
} else {
x = next;
}
}
}

template<typename Key, class Comparator>
SkipList<Key,Comparator>::SkipList(Comparator cmp, Arena* arena):compare_(cmp),arena_(arena),
head_(NewNode(0,kMaxHeight)),
max_height_(reinterpret_cast<void*>(1)),
rnd_(0xdeadbeef) {
for (int i = 0; i < kMaxHeight; i++){
head_->SetNext(i, NULL);
}
}

template<typename Key, class Comparator>
void SkipList<Key,Comparator>::Insert(const Key& key) {
// TODO(opt): We can use a barrier-free variant of FindGreaterOrEqual()
// here since Insert() is externally synchronized.
Node* prev[kMaxHeight];
Node* x = FindGreaterOrEqual(key, prev); //查找待插入的位置

// 不允许插入相同的数据
assert(x == NULL || !Equal(key, x->key));

int height = RandomHeight();
if (height > GetMaxHeight()) {
for (int i = GetMaxHeight(); i < height; i++) {
prev[i] = head_;
}
//fprintf(stderr, "Change height from %d to %d\n", max_height_, height);

// 并发的读操作可以在没有同步的情况下修改 max_height_。
// 如果一个并发的读要观察 max_height_ 的新值,会看到两种情况中的一种:
// (1)来自head_的新level指针的旧值,也就是NULL
// (2)或者是下面的循环设置的新值
// 在第一种情况中,读操作会直接下降到下一层,因为NULL排在所有key的后面
// 在第二种情况中,读操作会使用这个新的节点
max_height_.NoBarrier_Store(reinterpret_cast<void*>(height));
}

x = NewNode(key, height);
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].
x->NoBarrier_SetNext(i, prev[i]->NoBarrier_Next(i));
prev[i]->SetNext(i, x);
}
}

template<typename Key, class Comparator>
bool SkipList<Key,Comparator>::Contains(const Key& key) const {
Node* x = FindGreaterOrEqual(key, NULL);
if (x != NULL && Equal(key, x->key)) {
return true;
} else {
return false;
}
}

1.在 RandomHeight()kBranching(rnd_.Next() % kBranching 使得上层节点的数量约为下层的1/4。那么,当设定 MaxHeight=12 时,根节点为1时,约可均匀容纳 Key 的数量为 4^11= 4194304(约为400W)。当单独增大 MaxHeight 时,并不会使得 SkipList 的层级提升。MaxHeight=12为经验值,在百万数据规模时,尤为适用。

2.读值本身并不会改变SkipList的结构,因此多个读之间不存在并发问题。而当读、写同时存在时,SkipList 通过 AtomicPointer(原子指针)及结构调整上的小技巧达到“无锁”并发。一个节点一旦被添加到SkipList中,其层级结构将不再发生变化,Node中的唯一成员 port::AtomicPointer next_[1] 大小不会再发生改变。 port::AtomicPointer next_[1] 用于占位,实际的数组大小和本节点的 Height 一致。

3.跳表实际上是类似一种多层的有序链表,高层的链表比底层的链表节点更少,在更高层的链表上能更快的遍历完整个链表,跳到更底层的链表更利于精确的定位,以上便是skiplist利用空间换取时间的方法精髓。想首先从跳表头结点的最高层开始遍历,key值大于节点key值,则前往同一层的下一个节点,否则跳到节点的低一层并记录上一层的最后访问的节点,直到来到第一层(最底层)。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

R语言实战

R语言实战

卡巴科弗 (Robert I.Kabacoff) / 高涛、肖楠、陈钢 / 人民邮电出版社 / 2013-1 / 79.00元

数据时代已经到来,但数据分析、数据挖掘人才却十分短缺。由于“大数据”对每个领域的决定性影响, 相对于经验和直觉,在商业、经济及其他领域中基于数据和分析去发现问题并作出科学、客观的决策越来越重要。开源软件R是世界上最流行的数据分析、统计计算及制图语言,几乎能够完成任何数据处理任务,可安装并运行于所有主流平台,为我们提供了成千上万的专业模块和实用工具,是从大数据中获取有用信息的绝佳工具。  本书从解决......一起来看看 《R语言实战》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具