今天再來(lái)聊下redis中的有序集合sort set,也是redis里面最高級(jí)別的一種數(shù)據(jù)類型故河,之前有聊到集合(set) ,從名字上來(lái)看這兩種類型很相似巍佑,不過兩者可能在設(shè)計(jì)之初就被賦予了不同的使命吧色洞。顧名思義巫财,有序集合就是集合中的元素是按照一定的順序排列的妄田,是有序的净赴,我們之前說的那個(gè)集合是無(wú)序的绳矩,那接下來(lái)我們來(lái)詳細(xì)的看看redis 有序集合。
1.簡(jiǎn)介
有序集合(sort set)是在集合類型的基礎(chǔ)上為每個(gè)元素關(guān)聯(lián)一個(gè)分?jǐn)?shù)玖翅,那這使得我們不僅可以完成插入翼馆,刪除和判斷元素是否存在等集合操作,還能夠獲得分?jǐn)?shù)最高(或最低)的前N個(gè)元素金度,獲得指定分?jǐn)?shù)范圍內(nèi)的元素等分?jǐn)?shù)有關(guān)的操作应媚。雖然集合中每個(gè)元素都是不相同的,但是分?jǐn)?shù)可以相同猜极;內(nèi)部使用hashMap或者跳躍表來(lái)實(shí)現(xiàn)的中姜。下面給大家畫一張圖:
2. 命令
這里關(guān)于有序集合的命令寫得比較全,小伙伴們可以有選擇的看
- 增加元素/刪除元素/獲取元素的分?jǐn)?shù)/獲取集合中的元素?cái)?shù)量
zadd key score member [score member ...] score(分?jǐn)?shù))跟伏,如果分?jǐn)?shù)是小數(shù)的話可能會(huì)存在精度的問題丢胚,本人覺得比較省事的做法就是把數(shù)據(jù)轉(zhuǎn)換為整數(shù)后再進(jìn)行存放,比如乘以100受扳。
zrem key member [member] 携龟,返回成功刪除的元素?cái)?shù)量
zscore key member 返回指定元素的分?jǐn)?shù)
redis> zadd scoreboard 100 xiaoming 89 Tom 95 Peter // 往集合中加入三條記錄
(integer) 3
redis> zrem scoreboard xiaoming
(integer) 1
redis > zscore scoreboard Peter
"95"
redis > zcard scoreboard
(integer) 4
如果scoreboard 這個(gè)集合中已經(jīng)存在Tom這位同學(xué),那么執(zhí)行zadd 后會(huì)覆蓋原來(lái)的勘高。
- 獲得排名在某個(gè)范圍內(nèi)的元素列表/按照排名范圍刪除元素
zrange key start stop [withscores]
zrevrange key start stop [withscores]
zremrangebyrank key start stop 按照元素分?jǐn)?shù)從小到大的順序刪除處在指定排名范圍內(nèi)的所有元素峡蟋,并返回刪除的元素?cái)?shù)量浮定,注意是包含start和stop位置上的元素
redis > zrange scoreboard 0 -1 //獲取集合中的所有元素,并且按照分?jǐn)?shù)來(lái)從小到大排好序了层亿。
1) "Tom"
2) "Peter"
3) "xiaoming"
redis >zrange scoreboard 0 -1 withscores // 加上withscores參數(shù) 額外再返回分?jǐn)?shù)桦卒。
1) "Tom"
2) "89"
3) "Peter"
4) "95"
5) "xiaoming"
6) "100"
redis > zrevrange scoreboard 0 -1 //按照分?jǐn)?shù)從高到低排序。
1) "xiaoming"
2) "Peter"
3) "Tom"
redis > zadd testRem 1 a 2 b 3 c 4 d
(integer) 4
redis > zremrangebyrank testRem 0 2
(integer) 3
這里就展示這三個(gè)操作匿又,不過需要注意的是start和end 這兩個(gè)值的選取方灾,集合中的數(shù)據(jù)下標(biāo)是從0開始的,所以第一個(gè)元素標(biāo)號(hào)是0碌更,第二個(gè)元素標(biāo)號(hào)是1裕偿,zrange和zrevrange命令中的start和end返回的元素是包含start和end標(biāo)號(hào)下的元素的。所以我們?nèi)绻幌敕祷氐谝粋€(gè)元素可以這樣寫
redis> zrange scoreboard 0 0
1) "Tom"
還有一個(gè)需要注意的就是如果兩個(gè)元素的分?jǐn)?shù)相同痛单,Redis會(huì)按照字典順序(也就是 "0" < "9"<"A"<"Z"<"a"<"z" 這樣的順手來(lái)排列)來(lái)進(jìn)行排序嘿棘,但如果是中文則取決于中文的編碼方式,具體的沒做深入的研究旭绒,這里就不具體說了鸟妙。
不過貌似沒有只獲取所有元素分?jǐn)?shù)的命令
- 獲取指定分?jǐn)?shù)范圍內(nèi)的元素/獲取指定分?jǐn)?shù)范圍內(nèi)的元素個(gè)數(shù)/按照分?jǐn)?shù)范圍刪除元素
zrangebyscore key min max [WITHSCORES] [LIMIT offset count] //從小到大
zrevrangebyscore key min max [WITHSCORES] [LIMIT offset count] //從大到小
zcount key min max
zremrangebyscore key min max
redis > zrangebyscore scoreboard 80 100 WITHSCORES
1) "Tom"
2) "89"
3) "Peter"
4) "95"
5) "xiaoming"
6) "100"
redis > zcount scoreboard 80 90
(integer) 1
注意:包含min和max。 如果希望不要返回端點(diǎn)的數(shù)據(jù)可以使用"(" 符號(hào)挥吵。例如:
redis > zrangebyscore scoreboard 80 (100 WITHSCORES
1) "Tom"
2) "89"
3) "Peter"
4) "95"
redis >
如果需要返回大于某個(gè)值或者小于某個(gè)值的所有元素可以使用+inf和-inf 重父,分別表示正無(wú)窮大和負(fù)無(wú)窮大,比如希望返回高于89分忽匈,但是不包含89分的所有元素房午,但是你不知道最高分是多少,這個(gè)時(shí)候就可以這樣寫:
redis> zrangebyscore scoreboard (89 +inf WITHSCORES
1) "Peter"
2) "95"
3) "xiaoming"
4) "100"
zrangebyscore 這個(gè)命令還向我們提供了limit offset count 丹允,這個(gè)類型于我們經(jīng)常使用的sq語(yǔ)句里面的limit offset count郭厌,就是從offset這個(gè)文位置開始獲取count個(gè)元素
- 獲得元素的排名
zrank key member
zrevrank key member
redis > zrank scoreboard Peter
(integer) 2
redis > zrevrank scoreboard Peter
(integer) 1
注意:排名是從0開始計(jì)數(shù)的。
- 增加或者減少某個(gè)元素的分?jǐn)?shù)
zincrby key increment member 雕蔽,其中increment既可以為正數(shù)(表示增加)也可以為負(fù)數(shù)(表示減少),這個(gè)increment就好為整數(shù)折柠,不然也是會(huì)出現(xiàn)精度問題,導(dǎo)致數(shù)據(jù)不太對(duì)萎羔。
redis > zincrby scoreboard -1 Peter
"94"
6)計(jì)算有序集合的交集
ZINTERSTORE destination numkeys key [key ...] 液走,該命令用來(lái)計(jì)算多個(gè)有序集合的交集并將結(jié)果存放在destination鍵中(同樣以有序集合類型存儲(chǔ)),返回值為destination鍵中的元素個(gè)數(shù)贾陷。destination鍵中的元素分?jǐn)?shù)是有aggregate參數(shù)決定的缘眶。
(1) 當(dāng)aggregate是sum時(shí)(也就是默認(rèn)值),destination 鍵中的元素的分?jǐn)?shù)是每個(gè)參與計(jì)算的集合中的改元素分?jǐn)?shù)的和髓废。
redis > zadd sortedSets3 1 a 2 b
(integer) 2
redis > zadd sortedSets4 2 b 3 c
(integer) 2
redis > ZINTERSTORE sortedSetsResult1 2 sortedSets3 sortedSets4
(integer) 1
redis > zrange sortedSetsResult1 0 -1 WITHSCORES
1) "b"
2) "4"
(2) 當(dāng)aggregate是min或者max時(shí)巷懈,destination鍵中的元素的分?jǐn)?shù)是每個(gè)參與計(jì)算的集合中改元素分?jǐn)?shù)的最小值或者最小值。
redis > ZINTERSTORE sortedSetsResult2 2 sortedSets3 sortedSets4 AGGREGATE min
(integer) 1
redis > zrange sortedSetsResult2 0 -1 WITHSCORES
1) "b"
2) "2"
ZINTERSTORE 命令還能夠通過設(shè)置WEIGHTS參數(shù)設(shè)置每個(gè)集合的權(quán)重慌洪,每個(gè)集合在參與計(jì)算時(shí)元素的分?jǐn)?shù)會(huì)被乘上改集合的權(quán)重顶燕。
redis > ZINTERSTORE sortedSetsResult3 2 sortedSets3 sortedSets4 WEIGHTS 1 10
(integer) 1
redis > zrange sortedSetsResult3 0 -1 WITHSCORES
1) "b"
2) "22"
另外還有一個(gè)與ZINTERSTORE命令的用法相同的命令-zunionstore凑保,它的作用是計(jì)算集合間的并集,這里就不具體展示了涌攻。下面我們看下有序集合的相關(guān)應(yīng)用場(chǎng)景
3. 實(shí)踐
1. 實(shí)現(xiàn)按點(diǎn)擊量排序
以文章的ID作為鍵欧引,文章的點(diǎn)擊量作為分?jǐn)?shù),如果將鍵名命名為posts:page.view恳谎,每次訪問某篇文章的時(shí)候就zincrby posts:page.view 1 文章ID 來(lái)更新點(diǎn)擊量芝此,需要按照點(diǎn)擊量來(lái)顯示文章的時(shí)候 zrevrange posts:page.view start end 來(lái)獲取文章的某一范圍內(nèi)的文章id。
2. 按照文章的發(fā)布時(shí)間來(lái)排序
如果文章的發(fā)布時(shí)間是可以修改的因痛,我們可以使用有序集合來(lái)實(shí)現(xiàn)婚苹,元素是文章的id,元素的分?jǐn)?shù)是發(fā)布時(shí)間鸵膏,通過修改元素的對(duì)應(yīng)的分?jǐn)?shù)就可以達(dá)到更改時(shí)間的目的膊升。
3. 排行榜
比如要取得某個(gè)班級(jí)的前十名的學(xué)生名單,使用 zadd scoreboard <score> <username>將學(xué)生的分?jǐn)?shù)保存谭企,那么取得前十名的同學(xué)名單是很容易的廓译,zrevrange scoreboard 0 9
4. 可以做帶權(quán)重的隊(duì)列,比如普通消息的score為1赞咙,重要消息的score為2责循,然后工作線程可以選擇score的倒序來(lái)獲取工作任務(wù)糟港。讓重要的任務(wù)優(yōu)先執(zhí)行攀操。
4. 有序集合和集合的區(qū)別
首先他們都是集合,都支持添加秸抚,刪除速和,獲取元素。但是它們應(yīng)用的場(chǎng)景是不一樣的剥汤,
- Set(集合) 這種類型對(duì)于不同集間求交集颠放,并集,差集是很方便的吭敢,因此它非常適合用在一些社交的場(chǎng)景碰凶,比如某兩個(gè)人共同關(guān)注的人。
- Zset(有序集合)鹿驼,它更多的應(yīng)用于一些需要按照某種方式排序的情況下欲低,操作是很方便的,例如:求集合中分?jǐn)?shù)最高的幾個(gè)人的名單畜晰,使用zrevrange key 0 5
5. 有序集合和列表的區(qū)別
我們先來(lái)看下相同的地方:
- 兩者都是有序的砾莱,注意list的順序是按照插入的順序,而有序集合是按照分?jǐn)?shù)來(lái)排序的凄鼻,不是添加時(shí)候的順序腊瑟。
- 兩者都可以獲得某一范圍內(nèi)的元素聚假,說的具體一點(diǎn),就是list獲取某一范圍內(nèi)的元素是根據(jù)元素的位置來(lái)的闰非,有序集合可以獲取排名在某一范圍內(nèi)的元素膘格,也可以獲取分?jǐn)?shù)在某一范圍內(nèi)的元素。
下面來(lái)說下二者的區(qū)別 - list取鏈表的中間元素是相對(duì)沒有取兩端的數(shù)據(jù)塊财松,但是有序集合使用散列表或者跳躍表來(lái)作為底層實(shí)現(xiàn)闯袒,即使取中間的元素的速度也是很快的。
- 列表是不能簡(jiǎn)單的調(diào)整某個(gè)元素的位置的游岳,但是有序集合可以政敢,改變?cè)氐姆謹(jǐn)?shù)即可。我們知道list雖說是可以存儲(chǔ)各種列表胚迫,但是如果需要排序的話喷户,就可能比較麻煩
- 有序集合要比列表類型更消耗內(nèi)存。
關(guān)于有序集合就先寫到這吧访锻,如果有小伙伴需要補(bǔ)充的褪尝,歡迎在下面和我留言哦,看到會(huì)及時(shí)回復(fù)的期犬。