Redis設計與實現——數據結構(三)

跳躍表

跳躍表簡介

  • 跳躍表(skiplist)是一種有序數據結構, 它通過在每個節點中維持多個指向其他節點的指針, 從而達到快速訪問節點的目的。

  • 跳躍表支持平均 O(log N) 最壞 O(N) 複雜度的節點查找, 還可以通過順序性操作來批量處理節點。

  • 在大部分情況下, 跳躍表的效率可以和平衡樹相媲美, 並且因爲跳躍表的實現比平衡樹要來得更爲簡單, 所以有不少程序都使用跳躍表來代替平衡樹。

Redis 使用跳躍表作爲有序集合鍵的底層實現之一: 如果一個有序集合包含的元素數量比較多, 又或者有序集合中元素的成員(member)是比較長的字符串時, Redis 就會使用跳躍表來作爲有序集合鍵的底層實現。

跳躍表的實現

跳躍表節點的實現由 redis.h/zskiplistNode 結構定義:

typedef struct zskiplistNode {
    // 後退指針
    struct zskiplistNode *backward;
    // 分值
    double score;
    // 成員對象
    robj *obj;
    // 層
    struct zskiplistLevel {
        // 前進指針
        struct zskiplistNode *forward;
        // 跨度
        unsigned int span;
    } level[];
} zskiplistNode;

zskiplist 結構的定義如下:

typedef struct zskiplist {
    // 表頭節點和表尾節點
    struct zskiplistNode *header, *tail;
    // 表中節點的數量
    unsigned long length;
    // 表中層數最大的節點的層數
    int level;
} zskiplist;

這裏寫圖片描述

初看上去, 很容易以爲跨度和遍歷操作有關, 但實際上並不是這樣 —— 遍歷操作只使用前進指針就可以完成了, 跨度實際上是用來計算排位(rank)的: 在查找某個節點的過程中, 將沿途訪問過的所有層的跨度累計起來, 得到的結果就是目標節點在跳躍表中的排位。

注意表頭節點和其他節點的構造是一樣的: 表頭節點也有後退指針、分值和成員對象, 不過表頭節點的這些屬性都不會被用到, 所以圖中省略了這些部分, 只顯示了表頭節點的各個層。

分值和成員

  • 節點的分值(score 屬性)是一個 double 類型的浮點數, 跳躍表中的所有節點都按分值從小到大來排序。
  • 節點的成員對象(obj 屬性)是一個指針, 它指向一個字符串對象, 而字符串對象則保存着一個 SDS 值。

在同一個跳躍表中, 各個節點保存的成員對象必須是唯一的, 但是多個節點保存的分值卻可以是相同的: 分值相同的節點將按照成員對象在字典序中的大小來進行排序, 成員對象較小的節點會排在前面(靠近表頭的方向), 而成員對象較大的節點則會排在後面(靠近表尾的方向)。

參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章