有序集合
有序集合相對(duì)于哈希、列表店枣、集合來說會(huì)有一點(diǎn)點(diǎn)陌生速警,但既然叫有序集合,那么它
和集合必然有著聯(lián)系鸯两,它保留了集合不能有重復(fù)成員的特性闷旧,但不同的是,有序集合
中的元素可以排序钧唐。但是它和列表使用索引下標(biāo)作為排序依據(jù)不同的是忙灼,他給每個(gè)元
素設(shè)置一個(gè)分?jǐn)?shù)(score)作為排序的依據(jù)。下表為有序集合user:ranking钝侠,其中包
含kris该园、mike、frank帅韧、tim里初、martin、tom忽舟,他們的分?jǐn)?shù)分別是1双妨、91叮阅、200刁品、
220、250哑诊、251,有序集合提供了獲取指定分?jǐn)?shù)和元素范圍查詢暑劝、計(jì)算成員排名等功
能,合理的使用有序集合傅联,能幫助我們?cè)趯?shí)際開發(fā)中解決很多問題先改。
score | member |
---|---|
1 | kris |
91 | mike |
200 | frank |
220 | tim |
250 | martin |
251 | tom |
開發(fā)提示:有序集合中的元素不能重復(fù),但是score可以重復(fù)
下表給出列表蒸走、集合仇奶、有序集合三者的異同點(diǎn):
數(shù)據(jù)結(jié)構(gòu) | 是否允許重復(fù)元素 | 是否有序 | 有序?qū)崿F(xiàn)方式 | 應(yīng)用場(chǎng)景 |
---|---|---|---|---|
列表 | 是 | 是 | 索引下標(biāo) | 時(shí)間軸、消息隊(duì)列等 |
集合 | 否 | 否 | 無 | 標(biāo)簽比驻、社交等 |
有序集合 | 否 | 是 | 分值 | 排行榜系統(tǒng)该溯、社交等 |
-
命令
- 集合內(nèi)
-
添加成員
zadd key score member [score member]
下面操作向有序集合user:ranking添加用戶tom和他的分?jǐn)?shù)251:
127.0.0.1:6379> zadd user:ranking 251 tom (integer) 1
返回結(jié)果代表成功添加成員的個(gè)數(shù):
127.0.0.1:6379> zadd user:ranking 1 kris 91 mike 200 frank 220 tim 250 martin (integer) 5
有關(guān)zadd命令有兩點(diǎn)需要注意:
-
Redis3.2位zadd命令添加了nx、xx别惦、ch狈茉、incr四個(gè)選項(xiàng):
- nx:member必須不存在,才可以設(shè)置成功掸掸,用于添加氯庆。
- xx:member必須存在,才可以設(shè)置成功猾漫,用于更新点晴。
- ch:返回此次操作后,有序集合元素和分?jǐn)?shù)發(fā)生變化的個(gè)數(shù)悯周。
- incr:對(duì)score做增加,相當(dāng)于后面介紹的zincrby陪竿。
有集合相比集合提供了排序字段禽翼,但是也產(chǎn)生了代價(jià),zadd的時(shí)間復(fù)雜度為O(log(n)),sadd的時(shí)間復(fù)雜度為O(1)族跛。
-
-
計(jì)算成員個(gè)數(shù)
zcard key
例如下面操作返回有序集合user:ranking的成員數(shù)為5闰挡,和集合類型的
scard命令一樣,zcard的時(shí)間復(fù)雜度為O(1).127.0.0.1:6379> zcard user:ranking (integer) 5
-
計(jì)算某個(gè)成員的分?jǐn)?shù)
zscore key member
tom的分?jǐn)?shù)為251礁哄,如果成員不存在則返回nil:
127.0.0.1:6379> zscore user:ranking tom "251" 127.0.0.1:6379> zscore user:ranking test (nil)
-
計(jì)算成員的排名
zrank key member
zrevrank key member
zrank是從分?jǐn)?shù)從低到高返回排名长酗,zrevrank反之。例如下面操作中桐绒,tom
在zrank和zrevrank分別排名第5名和第0(排名從0開始計(jì)算)夺脾。127.0.0.1:6379> zrank user:ranking tom (integer) 5 127.0.0.1:6379> zrevrank user:ranking tom (integer) 0
-
刪除成員
zrem key member [member ...]
下面操作將成員mike從有序集合user:ranking中刪除。
127.0.0.1:6379> zrem user:ranking mike (integer) 1
返回結(jié)果為成功刪除的個(gè)數(shù)茉继。
-
增加成員的分?jǐn)?shù)
zincrby key increment member
下面操作給tom增加了9分咧叭,分?jǐn)?shù)變?yōu)榱?60分:
127.0.0.1:6379> zincrby user:ranking 9 tom "260"
-
返回指定排名范圍的成員
zrange key start end [withscores]
zrevrange key start end [withscores]
有序集合是按照分值排名的,zrange是從低到高返回烁竭,zrevrange反之菲茬。
下面代碼排名最低的是三個(gè)成員,如果加上withscores選項(xiàng),同時(shí)會(huì)返回
成員的分?jǐn)?shù):127.0.0.1:6379> zrange user:ranking 0 2 withscores 1) "kris" 2) "1" 3) "frank" 4) "200" 5) "tim" 6) "220" 127.0.0.1:6379> 1) "tom" 2) "260" 3) "martin" 4) "250" 5) "tim" 6) "220"
-
返回指定分?jǐn)?shù)范圍的成員
zrangebyscore key min max [withscores] [limit offset count]
zrevrangebyscore key max min [withsocre] [limit offset count]
其中zrangebyscore按照分?jǐn)?shù)從低到高返回婉弹,zrevrangebyscore反之睬魂。例
如下面操作從低到高返回200到221分的成員,withscores現(xiàn)象會(huì)同時(shí)返回
每個(gè)成員的分?jǐn)?shù)镀赌。[limit offset count]選項(xiàng)可以限制輸出的其實(shí)位置
和個(gè)數(shù):127.0.0.1:6379> zrangebyscore user:ranking 200 221 withscore 1) "frank" 2) "200" 3) "tim" 4) "220" 127.0.0.1:6379> zrevrangebyscore user:ranking 200 221 withscores 1) "tim" 2) "220" 3) "frank" 4) "200"
-
返回指定分?jǐn)?shù)范圍成員個(gè)數(shù)
zcount key min max
下面操作返回200到221分的成員的個(gè)數(shù):
127.0.0.1:6379> zcount user:ranking 221 200 (integer) 2
-
刪除指定排名內(nèi)的升序元素
zremrangebyrank key start end
下面操作刪除第start到第end名的成員:
127.0.0.1:6379> zremrangebyrank user:ranking 0 2 (integer) 3
-
刪除指定分?jǐn)?shù)范圍的成員
zremrangebyscore key min max
下面操作將250分以上的成員全部刪除汉买,返回結(jié)果為成功刪除的個(gè)數(shù):
127.0.0.1:6379> zremrangebyscore user:ranking (250 +inf (integer) 2
- 集合間操作
下表為有序集合user:ranking:1
score member 1 kris 91 mike 200 frank 220 tim 250 martin 251 tom 下表為有序集合user:ranking:2
score member 8 james 77 mike 625 martin 888 tom 127.0.0.1:6379> zadd user:ranking:1 1 kris 91 mike 200 frank 220 tim 250 martin 251 tom (integer) 6 127.0.0.1:6379> zadd user:ranking:2 8 james 77 mike 625 martin 888 tom (integer) 4
-
交集
zinterstore destination numkeys key [key ...] [weights weight [weight ...]] [aggregate sum|min|max]
這個(gè)命令參數(shù)較多,下面分別進(jìn)行說明:
- destination:交集計(jì)算結(jié)果保存到這個(gè)鍵佩脊。
- numkeys:需要做交集計(jì)算鍵的個(gè)數(shù)蛙粘。
- key [key ...]:需要做交集計(jì)算的鍵。
- weights weight [weight ...]:每個(gè)鍵的權(quán)重威彰,在做交集計(jì)算式出牧,每個(gè)鍵中的每個(gè)member會(huì)將自己分?jǐn)?shù)乘以這個(gè)權(quán)重,每個(gè)鍵的權(quán)重默認(rèn)是1.
- aggregate sum|min|max:計(jì)算成員交集后歇盼,分值可以按照sum(和)舔痕、min(最小值)、max(最大值)做匯總豹缀,默認(rèn)值是sum伯复。
下面操作對(duì)user:ranking:1和user:ranking:2做交集,weights和
aggregate使用了默認(rèn)配置邢笙,可以看到目標(biāo)鍵user:ranking:1_inter_2
對(duì)分值做了sum操作:127.0.0.1:6379> zinteerstore user:ranking:1_inter_2 2 user:ranking:1 user:ranking:2 (integer) 3 127.0.0.1:6379> zrange user:ranking:1_inter_2 0 -1 withscores 1) "mike" 2) "168" 3) "martin" 4) "875" 5) "tom" 6) "1139"
如果想讓user:ranking:2的權(quán)重變?yōu)?.5啸如,并且聚合效果使用max,可以
執(zhí)行如下操作:127.0.0.1:6379> zinterstore user:ranking:1_inter_2 2 user:ranking:1 user:ranking 2 weights 1 0.5 aggregate max (integer) 3 127.0.0.1:6379> zrange user:ranking:1_inter_2 0 -1 withscores 1) "mike" 2) "91" 3) "martin" 4) "312.5" 5) "tom" 6) "444"
-
并集
zunionstore destination numkeys key [key ...] [weights weight [weight ...]] [aggregate sum|min|max]
該命令的所有參數(shù)和zinterstore是一致的氮惯,只不過是做并集計(jì)算叮雳,例如
下面操作是計(jì)算user:ranking:1和user:ranking:2的并集,weights和
aggregate使用了默認(rèn)配置妇汗,可以看到目標(biāo)鍵user:ranking:1_union_2
對(duì)分值做了sum操作:127.0.0.1:6379> zunionstore user:ranking:1_union_2 2 user:ranking:1 user:ranking:2 (integer) 7 127.0.0.1:6379> zrange user:ranking:1_union_2 0 -1 withscores 1) "kris" 2) "1" 3) "james" 4) "8" 5) "mike" 6) "168" 7) "frank" 8) "200" 9) "tim" 10) "220" 11) "martin" 12) "875" 13) "tom" 14) "1139"
下表為這些命令的時(shí)間復(fù)雜度:
命令 時(shí)間復(fù)雜度 zadd key score member [score member ...] O(k*log(n)),k是添加成員的個(gè)數(shù)帘不,n是當(dāng)前有序集合成員個(gè)數(shù) zcard key O(1) zscore key member O(1) zrank key member O(log(n)),n是當(dāng)前有序集合成員個(gè)數(shù) zrevrank key member O(log(n)),n是當(dāng)前有序集合成員個(gè)數(shù) zrem key member [member ...] O(k*log(n)),k是添加成員的個(gè)數(shù),n是當(dāng)前有序集合成員個(gè)數(shù) zincrby key increment member O(log(n)),n是當(dāng)前有序集合成員個(gè)數(shù) zrange key start end [withscores] O(log(n) + k),k是要獲取的成員個(gè)數(shù)杨箭,n是當(dāng)前有序集合成員個(gè)數(shù) zrevrange key start end [withscores] O(log(n) + k),k是要獲取的成員個(gè)數(shù)寞焙,n是當(dāng)前有序集合成員個(gè)數(shù) zrangebysocre key min max [withscores] O(log(n) + k),k是要獲取的成員個(gè)數(shù),n是當(dāng)前有序集合成員個(gè)數(shù) zrevrangebyscore key max min [withscores] O(log(n) + k),k是要獲取的成員個(gè)數(shù)互婿,n是當(dāng)前有序集合成員個(gè)數(shù) zcount O(log(n)),n是當(dāng)前有序集合成員個(gè)數(shù) zremrangebyrank key start end O(log(n) + k),k是要?jiǎng)h除的成員個(gè)數(shù)捣郊,n是當(dāng)前有序集合成員個(gè)數(shù) zremrangebyscore key min max O(log(n) + k),k是要?jiǎng)h除的成員個(gè)數(shù),n是當(dāng)前有序集合成員個(gè)數(shù) zinterstore destination numkeys key [key ...] O(n*k) + O(m * log(m)),n是成員數(shù)最小的有序集合成員個(gè)數(shù)擒悬,k是有序集合的個(gè)數(shù)模她,m是結(jié)果集中成員個(gè)數(shù) zunionstore destination numkeys key [key ...]O(n*k) + O(m * log(m)),n是成員數(shù)最小的有序集合成員個(gè)數(shù),k是有序集合的個(gè)數(shù)懂牧,m是結(jié)果集中成員個(gè)數(shù)
-
內(nèi)部編碼
有序結(jié)合類型的內(nèi)部編碼有兩種:
ziplist(壓縮列表):當(dāng)有序集合的元素個(gè)數(shù)小于
zset-max-ziplist-entries配置(默認(rèn)128個(gè))侈净,同時(shí)每個(gè)元素的值都小于
zset-max-ziplist-value配置(默認(rèn)64字節(jié))時(shí)尊勿,Redis會(huì)用ziplist來作為
有序集合的內(nèi)部實(shí)現(xiàn),ziplist可以有效減少內(nèi)存的使用畜侦。skiplist(跳躍表):當(dāng)ziplist條件不滿足是元扔,有序結(jié)合會(huì)使用skiplist
作為內(nèi)部實(shí)現(xiàn),因?yàn)榇藭r(shí)ziplist的讀寫效率會(huì)下降旋膳。
下面用實(shí)例來說明:
1)當(dāng)元素個(gè)數(shù)較少且每個(gè)元素較小時(shí)澎语,內(nèi)部編碼為ziplist:
127.0.0.1:6379> zadd zsetkey 50 e1 60 e2 30 e3 (integer) 3 127.0.0.1:6379> object encoding zsetkey "ziplist"
2.1)當(dāng)元素超過128個(gè),內(nèi)部編碼變?yōu)閟kiplist:
127.0.0.1:6379> zadd zsetkey 50 e1 60 e2 30 e3 12 e4 ...忽略... 84 e 129 (integer) 129 127.0.0.1:6379> object encoding zsetkey "skiplist"
2.2)當(dāng)某個(gè)元素大于64字節(jié)是验懊,內(nèi)部編碼也會(huì)變?yōu)閟kiplist:
127.0.0.1:6379> zadd zsetkey 20 "one string is bigger than 64 byte..." (integer) 1 127.0.0.1:6379> object encoding zsetkey "skiplist"
-
使用場(chǎng)景
有序結(jié)合比較典型的使用場(chǎng)景就是排行榜系統(tǒng)擅羞。例如視頻網(wǎng)站需要對(duì)用戶上傳的
視頻做排行榜,榜單的維度可能是多個(gè)方面的:按照時(shí)間义图、按照播放數(shù)量减俏、按照
獲得的贊數(shù)。本節(jié)使用在贊數(shù)這個(gè)維度碱工,記錄每天用戶上傳視頻的排行榜娃承。主要
需要實(shí)現(xiàn)以下4個(gè)功能。(1)添加用戶贊數(shù)
例如用戶mike上傳了一個(gè)視頻怕篷,并獲得了3個(gè)贊历筝,可以使用有序集合的zadd
和zincrby功能:`zad user:ranking:2016_03_15 3 mike`
如果之后再獲得一個(gè)贊,可以使用zincrby:
`zincrby user:ranking:2016_03_15 1 mike`
(2)取消用戶在贊數(shù)
由于各種原因(例如用戶注銷廊谓、用戶作弊)需要將用戶刪除梳猪,此時(shí)需要將
用戶從榜單中刪除掉,可以使用zrem蹂析。例如刪除成員tom:`zrem user:ranking:2016_03_15 tom`
(3)展示獲取贊數(shù)最多的十個(gè)用戶
此功能使用zrevrange命令實(shí)現(xiàn):
`zrevrangebyrank user:rankin:2016_03_15 0 9`
(4)展示用戶信息以及用戶分?jǐn)?shù)
此功能將用戶名作為鍵后綴舔示,將用戶信息保存在哈希類型中,至于用戶的
分?jǐn)?shù)和排名可以使用zsore和zrank兩個(gè)功能:`hgetall user:info:tom` `zsocre user:ranking:2016_03_15 mike` `zrank user:ranking:2016_03_15 mike`