一、有序集合對象概述
它保留了集合不能有重復成員的特性, 但不同的是,有序集合中的元素可以排序。但是它和列表使用索引下標作為排序依據不同的是,它給每個元素設置一個分數(score)作為排序的依據
如下圖所示,該有序集合包含kris、mike、frank、tim、martin、tom, 它們的分數分別是1、91、200、220、250、251,有序集合提供了獲取指定 分數和元素范圍查詢、計算成員排名等功能,合理的利用有序集合,能幫助我們在實際開發中解決很多問題
? ? ? ? ??? ??? ?
特點:
有序集合中的鍵被稱為“成員”,成員的值成為“分值”,分值必須為浮點數
散列只能通過鍵訪問元素。但是有序集合既可以通過鍵訪問元素,也可以根據分值以及分值的排列順序來訪問元素的結構
和散列一樣,都是用于存儲鍵值對,鍵值不允許重復
與散列的不同:
下圖列出了列表、集合和有序集合三者的異同點:
二、命令
命令
zadd:添加成員。返回結果代表成功添加成員的個數
有關zadd命令有兩點需要注意:
nx:member必須不存在,才可以設置成功,用于添加
xx:member必須存在,才可以設置成功,用于更新
ch:返回此次操作后,有序集合元素和分數發生變化的個數
incr:對score做增加,相當于后面介紹的zincrby
?Redis3.2為zadd命令添加了nx、xx、ch、incr四個選項:
有序集合相比集合提供了排序字段,但是也產生了代價,zadd的時間復雜度為O(log(n)),sadd的時間復雜度為O(1)
zadd key score member [score member ...]
?
zcard:計算成員個數。時間復雜度為O(1)
zcard key
zscore:計算某個成員的分數。如果成員不存在則返回nil
zscore key member
?
zrank、zrevrank:計算成員排名。zrank是從分數從低到高返回排名,zrevrank反之。排名從0開始
zrank key member
zrevrank key member
zrem:刪除成員。返回結果為成功刪除的個數
zrem key member [member ...]
?
zincrby:增加成員的分數
zincrby key increment member
zrange、zrevrange:返回指定排名范圍的成員
有序集合是按照分值排名的,zrange是從低到高返回,zrevrange反之
如果加上withscores選項,同時會返 回成員的分數
zrange key start end [withscores]
zrevrange key start end [withscores]
zrangebyscore、zrevrangebyscore:返回指定分數范圍的成員
其中zrangebyscore按照分數從低到高返回,zrevrangebyscore反之
[limit offset count]選項可以限制輸出的起始位置和個數
同時min和max還支持開區間(小括號)和閉區間(中括號),-inf和 +inf分別代表無限小和無限大
zrangebyscore key min max [withscores] [limit offset count]
zrevrangebyscore key max min [withscores] [limit offset count]
?
zcount:返回指定分數范圍成員個數
zcount key min max
?
zremrangebyrank:刪除指定排名內的升序元素
zremrangebyrank key start end
?
zremrangebyscore:刪除指定分數范圍的成員
zremrangebyscore key min max
?
集合間的操作
將下圖的兩個有序集合導入到Redis中:
zinterstore:交集。參數如下:
destination:交集計算結果保存到這個鍵
numkeys:需要做交集計算鍵的個數
key[key...]:需要做交集計算的鍵
weights weight[weight...]:每個鍵的權重,在做交集計算時,每個鍵中 的每個member會將自己分數乘以這個權重,每個鍵的權重默認是1
aggregate sum|min|max:計算成員交集后,分值可以按照sum(和)、 min(最小值)、max(最大值)做匯總,默認值是sum
zinterstore destination numkeys key [key ...] [weights weight [weight ...]]
[aggregate sum|min|max]
下面操作對user:ranking:1和user:ranking:2做交集,weights和 aggregate使用了默認配置,可以看到目標鍵user:ranking:1_inter_2對分值 做了sum操作:
?如果想讓user:ranking:2的權重變為0.5,并且聚合效果使用max,可以 執行如下操作:
zunionstore:并集。該命令的所有參數和zinterstore是一致的,只不過是做并集計算
zunionstore destination numkeys key [key ...] [weights weight [weight ...]]
[aggregate sum|min|max]
例如 下面操作是計算user:ranking:1和user:ranking:2的并集,weights和 aggregate使用了默認配置,可以看到目標鍵user:ranking:1_union_2對分值 做了sum操作:?
下圖給出了有序集合命令的復雜度:
命令 | 時間復雜度 |
zadd? keyscoremember[scoremember...] | O(kXlo()),k是添加成員的個數,”是當前有序 集合成員個數 |
zcard key | 0(1) |
zscore key member | 0(1). |
zrank key member arevrank key member | 0(og(),”是當前有序集合成員個數 |
rem key member[member...1 | 0(k*1og()),k是刪除成員的個數,"是當前有序集 合成員個數 |
zincrby key increment member | O(log(m)),”是當前有序集合成員個數 |
zrange key? start? end[withscores] zrevrange key? start end[withscores] | O(log(m)+k),k是要獲取的成員個數,η是當前有 序集合成員個數 |
zrangebyscore key? min? max[withscores] zrevrangebyscore key max min[withscores] | 0(log(m)+k),k是要獲取的成員個數,η是當前有 序集合成員個數 |
zcount | 0(log(n)),"是當前有序集合成員個數 |
zremrangebyrank key start end | O(log(m)+k),k是要刪除的成員個數,”是當前有 序集合成員個數 |
zremrangebyscore key min max | O(log(n)+k),k是要刪除的成員個數,是當前有 序集合成員個數 |
zinterstore destinationnum keys key[key...1 | (n*k)+0O(m*log(m)),”是成員數最小的有序集合成 員個數,k是有序集合的個數,m是結果集中成員個數 |
zunionstore destinationnum keys key[key...] | 0()+O(m*log(m)),”是所有有序集合成員個數和, m是結果集中成員個數 |
三、內部編碼
有序集合類型的內部編碼有兩種:
ziplist(壓縮列表):當有序集合的元素個數小于zset-max-ziplistentries配置(默認128個),同時每個元素的值都小于zset-max-ziplist-value配 置(默認64字節)時,Redis會用ziplist來作為有序集合的內部實現,ziplist 可以有效減少內存的使用
skiplist(跳躍表):當ziplist條件不滿足時,有序集合會使用skiplist作 為內部實現,因為此時ziplist的讀寫效率會下降
演示說明
當元素個數較少且每個元素較小時,內部編碼為skiplist:
當元素個數超過128個,內部編碼變為ziplist
當某個元素大于64字節時,內部編碼也會變為skiplist:
四、應用場景
排行榜
有序集合比較典型的使用場景就是排行榜系統。例如游戲里經常要對用戶的副本關卡得分,聲望,戰力,段位等做排行榜,榜單的維度可能是多個方面的:按照等級,最后分數更新時間。本節使用等級這個維度,記錄每天用戶副本星級的排行榜。主要需要實現以下4個功能
①添加玩家星級。例如玩家mike打副本勝利獲得了3顆星,可以使用有序集合的zadd和zincrby功能:
zadd user:ranking mike 3
如果之后再獲得一個星星,可以使用zincrby:
zincrby user:ranking mike 1
②取消用戶星級排行信息。由于各種原因(例如用戶注銷、用戶作弊)需要將用戶刪除,此時需要 將用戶從榜單中刪除掉,可以使用zrem。例如刪除成員tom:
zrem user:ranking mike
③展示獲取前十名用戶。此功能使用zrevrange命令實現:
zrevrangebyrank user:ranking 0 9
④展示用戶信息以及用戶分數。此功能將用戶名作為鍵后綴,將用戶信息保存在哈希類型中,至于用戶的分數和排名可以使用zscore和zrank兩個功能:
hgetall user:info:tom
zscore user:ranking mike
zrank user:ranking mike
時間線
在互聯網上,有很多網站都會根據內容的發布時間來對內容進行排序,比如:
博客系統會按照文章發布時間的先后,把最近發布的文章放在前面,而發布時間較早的文章則放 在后面,這樣訪客在瀏覽博客的時候,就可以先閱讀最新的文章,然后再閱讀較早的文章
新聞網站會按照新聞的發布時間,把最近發生的新聞放在網站的前面,而早前發生的新聞則放在網站的后面,這樣當用戶訪問該網站的時候,就可以第一時間查看到最新的新聞報道
諸如微博和Twitter 這樣的微博客都會把用戶最新發布的消息放在頁面的前面,而稍早之前發布的 消息則放在頁面的后面,這樣用戶就可以通過向后滾動網頁,查看最近一段時間自己關注的人都發表了哪些動態