redis數據類型--zset


數據結構:

zset是有序的,不運行重復的,帶有分值score的

數據結構對比:

 

操作命令:

zadd myzset 10 java 20 python 20 ruby 40 mysql 50 php (添加)

zrange myzset 0 -1 withscores (通過score進行排序從小到大)

zrevrange myzset 0 -1 withscores (通過score進行排序從大到小)

zrangebyscore myzset 20 30 (通過score取出20-30的產品)

zrem myzset java python (將java和python移除)

zscard myzset (統計總數)

zincrby myzset 5 java (將java加5分)

zcount myzset 20 50 (統計20-50之間有多少個)

zrank myzset java (java在set中處於第幾個位置,從0開始)

zscore myzset java (java有多少分)

存儲實現原理:

在redis.conf文件中有兩行這么寫的

zset-max-ziplist-entries 128  # zset中壓縮列表ziplist最大的元素是128個,超過128將會使用跳表skiplist+dict來存儲

zset-max-ziplist-value 64 # zset中壓縮列表ziplist中每個元素最大的個數是64個,超過64將會使用跳表skiplist+dict來存儲

什么是跳表skiplist dict呢?我們先來看下面一張圖:

這是一個有序的鏈表,當我們要將20這個數字插入到鏈表中的時候,他是從開始到結尾一個一個進行比對直到找到21之后才停止,這樣他的效率就很低,時間復雜度是O(n),查找也是一樣的道理。

怎么優化呢,skiplist是怎么實現的呢?再來看下面一張圖:

假如我們每相鄰兩個節點增加一個指針(或者理解為有三個元素進入了第二層),讓指針指向下下個節點。

這樣所有新增加的指針連成了一個新的鏈表,但它包含的節點個數只有原來的一半(上圖中是 7, 19, 26)。在插入一個數據的時候,
決定要放到那一層,取決於一個算法(在 redis 中 t_zset.c 有一個 zslRandomLevel 這個方法)。現在當我們想查找數據的時候,可以先沿着這個新鏈表進行查找。當碰到比待查數
據大的節點時,再回到原來的鏈表中的下一層進行查找。比如,我們想查找 23,查找的路徑是沿着下圖中標紅的指針所指向的方向進行的:

1. 23 首先和 7 比較,再和 19 比較,比它們都大,繼續向后比較。
2. 但 23 和 26 比較的時候,比 26 要小,因此回到下面的鏈表(原鏈表),與 22比較。
3. 23 比 22 要大,沿下面的指針繼續向后和 26 比較。23 比 26 小,說明待查數據 23 在原鏈表中不存在,在這個查找過程中,由於新增加的指針,我們不再需要與鏈表中每個節點逐個進行比較了。需要比較的節點數大概只有原來的一半。這就是跳躍表。

可以看到他將某些元素中加了指針,這有點類似於數組中的二分法查找,但是鏈表中沒有數組,所以用指針的方式來實現,看下面源碼

源碼位置:server.h
typedef struct zskiplistNode {
    sds ele; /* zset 的元素 */
    double score; /* 分值 */
    struct zskiplistNode *backward; /* 后退指針 */
    struct zskiplistLevel {
        struct zskiplistNode *forward; /* 前進指針,對應 level 的下一個節點 */
        unsigned long span; /* 從當前節點到下一個節點的跨度(跨越的節點數) */
    }level[]; /**/
}zskiplistNode;

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

typedef struct zset {
    dict *dict;
    zskiplist *zsl;
} zset;

隨機獲取層數的函數,源碼位置t_zset.c

int zslRandomLevel(void) {
    int level = 1;
    while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
        level += 1;
    return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL; 
}

應用場景:

排行榜

id 為 6001 的新聞點擊數加 1:zincrby hotNews:20190926 1 n6001
獲取今天點擊最多的 15 條:zrevrange hotNews:20190926 0 15 withscores

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM