Redis zset底層結構

2021-10-11 18:05:27 字數 1798 閱讀 3183

redis中 zset 底層採用雜湊表+跳躍列表(skiplist)來儲存資料。

雜湊表不用多說,set 底層採用雜湊表來儲存,value都為null,通過雜湊表key的唯一性保證set中元素的不重複。

上圖就是跳躍列表的示意圖,圖中只畫了四層,redis 的跳躍表共有 64 層,意味著最 多可以容納 2^64 次方個元素。

每乙個 kv 塊對應的結構如下面的**中的 zslnode 結構,kv header 也是這個結構,只不過 value 欄位是 null 值,score為double.min_value,用來墊底的。

底層的 kv 之間使用指標串起來形成了雙向鍊錶結構,它們是有序排列的,從小到大。

struct zslnode
不同的 kv 層高可能不一樣,層數越高的 kv 越少。同一層的 kv 會使用指標串起來。每乙個層元素的遍歷都是從 kv header 出發。

隨機層數

對於新插入的節點,需要呼叫乙個隨機演算法分配合理的層數。

直觀上期望的目標是 50% 的 level1,25% 的 level2,12.5% 的 level3,一直到最頂層 2^-63,因為這裡每一層的晉公升概率是 50%。

不過 redis 標準原始碼中的晉公升概率只有 25%,也就是**中的 zskiplist_p 的值。所以官方的跳躍列表更加的扁平化,層高相對較低,在單個層上需要遍歷的節點數量會稍多一點。

int zslrandomlevel(void) {

int level = 1;

while ((random()&0xffff) < (zskiplist_p * 0xffff))

level += 1;

return (level因為層數一般不高,所以遍歷的時候從頂層開始往下遍歷會非常浪費。跳躍列表會記錄一下當前的最高層數 maxlevel,遍歷時從這個 maxlevel 開始遍歷性能就會提高很多。

查詢過程

通過逐層查詢的方式來查詢資料,時間複雜度為 o(logn)

如圖所示,我們要定位到那個紫色的 kv,需要從 header 的最高層開始遍歷找到第乙個節點 (最後乙個比「我」小的元素)。

然後從這個節點開始降一層再遍歷找到第二個節點 (最後乙個比「我」小的元素)。

然後一直降到最底層進行遍歷就找到了期望的節點 (最底層的最後乙個比「我」小的元素)。

插入過程

查詢過程中已經找到 最底層的最後乙個比「我」小的元素,插入時,只要在這個元素後插入元素即可。

插入時,需修改前後元素的指標。如果新節點的高度大於當前的最大高度,需更新當前的最大高度。

如果所有元素的score值都相同呢?

在乙個極端的情況下,zset 中所有的 score 值都是一樣的,zset 的查詢效能會退化為o(n) 麼?redis 作者自然考慮到了這一點,所以 zset 的排序元素不只看 score 值,如果score 值相同還需要再比較 value 值 (字串比較)。

有序集合 REDIS ZSET

命令 command key score value zadd myzset 1 one key是作為db最上層字典索引的key 儲存到底層的是score和value 1.如果第乙個元素符合以下條件的話,就建立乙個 redis encoding ziplist 編碼的有序集 redis zset底層...

Redis Zset有序集合

向有序集合新增乙個或多個成員,或者更新已存在成員的分數 zadd zset集合 score1 v1.scoren vn通過索引區間返回有序集合成指定區間內的成員 zrange zset集合 start stop withscores 通過分數返回有序集合指定區間內的成員 zrangebyscore ...

redis Zset有序集合

127.0.0.1 6379 zadd zset 1 one 新增乙個元素 integer 1 127.0.0.1 6379 zadd zset 2 two 3 three 新增多個元素 integer 2 127.0.0.1 6379 zrange zset 0 1 1 one 2 two 3 t...