leveldb原始碼分析4:SkipList
阿新 • • 發佈:2019-01-29
skiplist思想可以具體參考這:
// Create a new SkipList object that will use "cmp" for comparing keys, // and will allocate memory using "*arena". Objects allocated in the arena // must remain allocated for the lifetime of the skiplist object. 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;
private成員變數:
我們下面來首先分析初始化操作,如下:// 最大的level enum { kMaxHeight = 12 }; // Immutable after construction Comparator const compare_; // 記憶體分配器 Arena* const arena_; // Arena used for allocations of nodes // 指向第一個節點,建構函式中初始化 Node* const head_; // Modified only by Insert(). Read racily by readers, but stale // values are ok. port::AtomicPointer max_height_; // Height of the entire list
// 初始化: // 1. 初始化compare_ // 2. 初始化arena_ // 3. 初始化head_,指向指標陣列 // 4. 初始化max_height_ // 5. 初始化rnd_隨機數的seed // 6. 初始化head_指向的陣列 template<typename Key, class Comparator> SkipList<Key,Comparator>::SkipList(Comparator cmp, Arena* arena) : compare_(cmp), arena_(arena), head_(NewNode(0 /* any key will do */, kMaxHeight)), max_height_(reinterpret_cast<void*>(1)), rnd_(0xdeadbeef) { // 初始化head_指向的陣列 for (int i = 0; i < kMaxHeight; i++) { head_->SetNext(i, NULL); } }
下面是一個插入操作的示意圖:
leveldb中實現的插入程式碼就是按照上面的思路實現,首先查詢到合適的位置,並記錄查詢過程中經過的路徑,之後新生成一個節點,修改指標。
// 插入操作
// 這裡的key其實已經是經過處理的key,包含了使用者指定的key和value
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.
// prev記錄的是查詢路徑,下面需要使用prev來修改新生成
// 節點的指標
Node* prev[kMaxHeight];
Node* x = FindGreaterOrEqual(key, prev);
// Our data structure does not allow duplicate insertion
// 不允許插入重複的值
assert(x == NULL || !Equal(key, x->key));
// 隨即生成節點高度
int height = RandomHeight();
// 對prev陣列中未賦值的元素進行賦值
if (height > GetMaxHeight()) {
for (int i = GetMaxHeight(); i < height; i++) {
prev[i] = head_;
}
// It is ok to mutate max_height_ without any synchronization
// with concurrent readers. A concurrent reader that observes
// the new value of max_height_ will see either the old value of
// new level pointers from head_ (NULL), or a new value set in
// the loop below. In the former case the reader will
// immediately drop to the next level since NULL sorts after all
// keys. In the latter case the reader will use the new node.
// 設定max_height變數
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節點的指標
x->NoBarrier_SetNext(i, prev[i]->NoBarrier_Next(i));
prev[i]->SetNext(i, x);
}
}
函式FindGreaterOrEqual中完成查詢操作,就是向下(level控制)和向右(x控制)移動過程,並不斷經經過路徑儲存到引數prev中。
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)) { // 向右移動
// 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--;
}
}
}
}
查詢操作基本上就是呼叫函式上面的函式FindGreaterOrEqual實現:
// 查詢操作
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;
}
}
上面基本上就是skiplist在leveldb中實現,leveldb中沒有使用複雜的紅黑樹等機制去保證資料的有序性,而是使用了輕快的skiplist實現。最後需要注意skiplist中每個節點儲存key是使用者傳遞keyvalue經過變幻(變幻方法參考http://blog.csdn.net/xuqianghit/article/details/6948164)得到的。