日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

虚线 实现_redis跳跃表实现

發布時間:2023/12/4 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 虚线 实现_redis跳跃表实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

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

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

redis 只在兩個地方使用到了跳躍表,一個是實現有序集合鍵,另一個是在集群節點中用作內部數據結構。

1? ?跳躍表的實現

redis 的跳躍表由? redis.h/zskiplistNode?和?redis.h/zskiplist 兩個結構定義,其中 zskiplistNode 結構用于表示跳躍表節點,而 zskiplist 結構則用于保存跳躍表節點的相關信息,比如節點的數量,以及指向表頭節點和表尾節點的指針等等。


上面最左邊是一個??zskiplist?結構。該結構包含以下屬性:

1、header:指向跳躍表的表頭節點。

2、tail:指向跳躍表的表尾節點。

3、level:記錄目前跳躍表內,層數最大的那個節點的層數(表頭節點的層數不計算在內)。

4、length:記錄跳躍表的長度,也就是跳躍表目前包含節點的數量(表頭節點的層數不計算在內)。

位于 zskiplist 右方的是? 4 個 zskiplistNode 結構。該結構包含以下屬性:

1、層(level):節點中用 L1、L2 等字樣標記節點的各個層,L1 代表第一層,L2 代表第二層,以此類推。每個層都帶有兩個屬性:前進指針和跨度。

前進指針用于訪問位于表尾方向的其他節點,而跨度則記錄了前進指針所指向節點和當前節點的距離。在上面圖片中,連線上帶有數字的箭頭就代表前進指針,而那個數字則代表跨度。

2、后退(backward)指針:節點中用 BW?字樣標記節點的后退指針,它指向位于當前節點的前一個節點。后退指針在程序從表尾向表頭遍歷時使用。

3、分值(score):各個節點中的 1.0、2.0 是節點所保存的分值。在跳躍表中,節點按各自保存的分值從小到大排列。

4、成員對象(obj):各個節點的 o1、o2 是節點保存的成員對象。

注意:

表頭節點跟其他節點的構造是一樣的,只不過表頭節點的這些屬性不會用到,因此省略了這部分。

1.1? ? 跳躍表節點

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

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

1、層

跳躍表節點的 level 數組可以包含多個元素,每個元素都包含一個指向其他節點的指針,程序可以通過這些層來加快訪問其他節點的速度。一般來說,層的數量越多,訪問其他節點的速度就越快。

每次創建一個新的跳躍表節點的時候,程序都會根據冪次定律(越大的數出現的概率越小)隨機生成一個介于 1 和 32 之間的值作為?level?數組的大小,這個大小就是層的“高度”。

2、前進指針

每個層都有一個指向表尾方向的前進(level[i].forward 屬性),用于從表頭向表尾方向訪問節點。

下面展示了帶有不同層高的節點:

下面用虛線表示出了程序從表頭向表尾方向,遍歷跳躍表中所有節點的路徑:

1、迭代程序首先訪問跳躍表的第一個節點(表頭),然后從第四層的前進指針移動到表中的第二個節點。?

2、在第二個節點時,程序沿著第二層的前進指針移動到表中的第三個節點。?

3、?在第三個節點時,程序同樣沿著第二層的前進指針移動到表中的第四個節點。?

4、當程序再次沿著第四個節點的前進指針移動時,它碰到一個?NULL,程序知道這時已經到達了跳躍表的表尾,于是結束這次遍歷。

3、跨度

層的跨度(?level[i].span 屬性)用于記錄兩個節點之間的距離:

1、兩個節點之間的跨度越大,它們相距得就越遠。

2、指向?NULL 的所有前進指針的跨度都為0,因為它們沒有連向任何節點。

跨度實際上是用來計算排位(rank)的。在查找某個節點的過程中,將沿途訪問過的所有層的跨度累計起來,得到的結果就是目標節點在跳躍表中的排位。

舉個例子,下圖用虛線標記了在跳躍表中查找分值為?2.0、成員對象為?o2 的節點時,沿途經歷的層:在查找節點的過程中,程序經過了兩個跨度為1的節點,因此可以計算出,目標節點在跳躍表中的排位為2。

4、后退指針

節點的后退指針(backward 屬性)用于從表尾向表頭方向訪問節點。


跟可以一次跳過多個節點的前進指針不同,因為每個節點只有一個后退指針,所以每次只能后退至前一個節點。

下圖用虛線展示了如何從表尾向表頭遍歷跳躍表中的所有節點:

程序首先通過跳躍表的tail指針訪問表尾節點,然后通過后退指針訪問倒數第二個節點,之后再沿著后退指針訪問倒數第三個節點,再之后遇到指向?NULL 的后退指針,于是訪問結束。

5、分值和成員

節點的分值(score屬性)是一個?double 類型的浮點數,跳躍表中的所有節點都按分值從小到大來排序。

節點的成員對象(obj屬性)是一個指針,它指向一個字符串對象,而字符串對象則保存著一個?SDS 值。

在同一個跳躍表中,各個節點保存的成員對象必須是唯一的,但是多個節點保存的分值卻可以是相同的:

分值相同的節點將按照成員對象在字典中的大小來進行排序,成員對象較小的節點會排在前面(靠近表頭的方向),而成員對象較大的節點則會排在后面(靠近表尾的方向)。

在下圖中所示的跳躍表中,三個跳躍表節點都保存了相同的分值10086.0。

但保存成員對象o1的節點卻排在保存成員對象o2和o3的節點的前面,而保存成員對象o2的節點又排在保存成員對象o3的節點之前,由此可見,o1、o2、o3三個成員對象在字典中的排序為o1<=o2<=o3。

1.2????跳躍表

靠多個跳躍表節點就可以組成一個跳躍表,如下所示:

zskiplist 結構的定義如下:

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

通過使用一個?zskiplist 結構來持有這些節點,程序可以更方便地對整個跳躍表進行處理。比如快速訪問跳躍表的表頭節點和表尾節點,或者快速地獲取跳躍表節點的數量(也即是跳躍表的長度)等信息,如下圖所示:

這樣獲取表頭、表尾節點,表長,以及表中最高層數的復雜度均為?O(1)。

2? ?跳躍表 API

3? ? 回顧

  • 跳躍表是有序集合的底層實現之一, 除此之外它在 Redis 中沒有其他應用。

  • Redis 的跳躍表實現由?zskiplist?和?zskiplistNode?兩個結構組成, 其中?zskiplist?用于保存跳躍表信息(比如表頭節點、表尾節點、長度), 而?zskiplistNode?則用于表示跳躍表節點。

  • 每個跳躍表節點的層高都是?1?至?32?之間的隨機數。

  • 在同一個跳躍表中, 多個節點可以包含相同的分值, 但每個節點的成員對象必須是唯一的。

  • 跳躍表中的節點按照分值大小進行排序, 當分值相同時, 節點按照成員對象的大小進行排序。

參考資料

[1]??redis設計與實現

總結

以上是生活随笔為你收集整理的虚线 实现_redis跳跃表实现的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。