Redis06 底層 跳躍鍊錶skiplist

2022-08-19 16:12:11 字數 3249 閱讀 7723

每次初始化時初始化最初的節點,也就是最左邊的節點,它的高度就是跳表最大高度,初始每個位置指標指向null。

插入元素時首先判斷該元素在集合中是否存在,從最初節點的最高位置開始尋找,向前的指標鍵值比要找的值大就向下移動,小就向前移動。如果不存在就插入,首先用隨機函式每次隨機乙個數0或1,出現一次1就停止,出現0的個數就是層數。然後從初始節點的對應層開始,如果前乙個節點值大,就生成乙個node,令初始節點層指標指向node,node向前指向本來前面的值,如果前乙個節點值小就向下移動然後繼續判斷,直到走完第一層,對應節點的所有向前指標都已經建立完畢了。

刪除時只需要找到該節點該層誰指向它,把指標指向node前面的node即可。

跳表的node:

class skiplistnode

}

跳表list的字段和構造方法:

class skiplist

}

跳表的add方法:

//新增元素

public void add(integer newvalue)

while(level > maxlevel)

//建立node,確定head為查詢時的起始node

skiplistnode newnode = new skiplistnode(newvalue);

skiplistnode current = head;

//遍歷的時候每次要麼向前走,要麼向下走,按照層數遍歷

//每一層都要建立新node,然後連線前後指標

do while(level-- > 0);}}

查詢方法findnext:

//找到要找的目標值e在這一層level裡剛剛大於e的node,current是尋找的起始位置

private skiplistnode findnext(integer e, skiplistnode current, int level)

//如果本節點是8,前乙個節點是10,要找的值是12就會繼續向前找

//更新current和next

//每次如果前乙個節點比要找的值小就前進,否則找下一層

current = next;

next = current.nextnodes.get(level);

}return current;

}

contains方法:

public boolean contains(integer value) 

//找到e對應的節點

private skiplistnode find(integer e)

delete方法:

public void delete(integer deletevalue) 

}while(level-- > 0);}}

跳表的迭代器(遍歷結果一定是從小到大的):

class skiplistiterator implements iterator

@override

public boolean hasnext()

@override

public integer next()

}

在組織score時redis採用跳表,而從member到score的對映redis使用字典來儲存。

在redis中每一層的晉公升概率是25%,它是一種更扁平化的跳表,在單個層上遍歷的節點個數就會稍多一些。

在調整元素權重時,redis採用對該節點先刪後加的方式來進行。

如果權重都相同redis還會比較value值,使redis中的跳表有序。

redis計算元素排名rank時,是對跳表功能的一種加強,對於每個元素都有它的字段rank值,跳表在變化時會更新這個值。

在redis中跳躍表是有序集合sorted set的底層實現之一。跳躍表通過在每個節點中維持多個指向其他節點的指標,從而達到快速訪問節點的目的。支援平均ologn、最壞on複雜度的節點查詢,還可以通過順序性操作來批量處理節點。

redis的鍊錶由兩種結構組成,乙個是結點zskiplistnode,乙個是表zskiplist。下圖為乙個跳表例子:

最左邊就是乙個zskiplist,它包括header(指向跳躍表的表頭節點)、tail(指向跳躍表的表尾節點)、level(當前跳躍表的最大層數)、length(跳躍表的長度,也就是資料節點的數量)。

右側是4個zskiplistnode,它有幾個屬性:

1、層level,也就是l1/l2/../l4等,每個層都有兩個屬性,分別是指標(指向表尾方向的其他節點)、跨度(記錄跨度方向兩點的距離),當從表頭向表尾遍歷跳表時,就會借助前進指標。

2、後退指標bw,它用來指向當前節點的前乙個節點,在從表尾向表頭遍歷跳表時,就會借助後退指標。

3、分值:每個節點中的1.0、2.0、3.0。節點按照分值從小到大排列。

4、成員物件obj:o1、o2和o3,它是節點儲存的值。

zskiplistnode的定義如下:

typedef struct zskiplistnode level;

//後退指標

struct zskiplistnode *backward;

//分值

double score;

//成員物件

robj *obj;

} zskiplistnode;

每次生成乙個新跳躍表節點的時候,程式都會自動生成乙個介於1到32之間的值作為level陣列的大小,這個大小就是層的高度,每一層的晉公升概率是25%,這是乙個更偏於扁平的跳表。

span跨度這個欄位是為了計算某個元素的排位rank使用的,在查詢某個節點的過程中,將沿途訪問的所有層的跨度都累加起來,得到的結果就是目標節點在跳躍表中的排位。

obj是乙個指向sds的指標,在同乙個跳表中,各節點儲存的物件obj必須是唯一的,而分值score可以是相同的,分值相同的節點會按照物件obj的字典序大小來進行排序。

zskiplist的定義如下:

typedef struct zskiplist zskiplist;

Redis的跳躍表底層實現

跳躍表是一種有序的資料結構,主要用在zset 有序集合 和集群節點的內部資料結構。在大部分情況下,跳躍表的效率可以和平衡樹相媲美,並且因為跳躍表的實現比平衡樹要來得更為簡單,所以有不少程式都使用跳躍表來代替平衡樹。注意mysql的底層採用的是b 樹實現。跳躍表的實現 redis的跳躍表由redis....

Redis底層之鍊錶

redis底層鍊錶節點使用listnode來實現。每個listnode節點包含三個成員,乙個prev指標指向前乙個listnode,乙個next指標指向下乙個節點。乙個value指標指向任何型別的值。多個listnode通過prev和next指標組成雙端鍊錶。通過乙個list比較方便地來管理產生的l...

Redis底層資料結構(6)跳躍表

當乙個有序集合包含的元素數量多,又或者有序集合中的元素的成員是比較長的字串時,redis就會使用跳躍表作為有序集合鍵的底層實現。簡介如果我們要實現按照成績對學生進行排名,可以選擇陣列 鍊錶 平衡樹或紅黑樹來實現,陣列的插入和刪除效率低,鍊錶查詢的效率低,平衡樹或紅黑樹雖然效率高但是實現複雜。跳躍表 ...