數據結構:
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是怎么實現的呢?再來看下面一張圖:
可以看到他將某些元素中加了指針,這有點類似於數組中的二分法查找,但是鏈表中沒有數組,所以用指針的方式來實現,看下面源碼
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; }
應用場景:
排行榜
