Redis物件——有序集合(ZSet)

老於`發表於2020-04-17

有序集合型別 (Sorted Set或ZSet) 相比於集合型別多了一個排序屬性 score(分值),對於有序集合 ZSet 來說,每個儲存元素相當於有兩個值組成的,一個是有序結合的元素值,一個是排序值。有序集合保留了集合不能有重複成員的特性(分值可以重複),但不同的是,有序集合中的元素可以排序。

一、內部實現

有序集合是由 ziplist (壓縮列表)skiplist (跳躍表) 組成的。

當資料比較少時,有序集合使用的是 ziplist 儲存的,有序集合使用 ziplist 格式儲存必須滿足以下兩個條件:

  • 有序集合儲存的元素個數要小於 128 個;
  • 有序集合儲存的所有元素成員的長度都必須小於 64 位元組。

如果不能滿足以上兩個條件中的任意一個,有序集合將會使用 skiplist 結構進行儲存。

有關ziplist 和skiplist 這兩種redis底層資料結構的具體實現可以參考我的另外兩篇文章。

Redis資料結構——壓縮列表

Redis資料結構——跳躍表

二、常用命令

Redis列表物件常用命令如下表(點選命令可檢視命令詳細說明)。

命令 說明 時間複雜度
BZPOPMAX key [key ...] timeout 從一個或多個排序集中刪除並返回得分最高的成員,或阻塞,直到其中一個可用為止 O(log(N))
BZPOPMIN key [key ...] timeout 從一個或多個排序集中刪除並返回得分最低的成員,或阻塞,直到其中一個可用為止 O(log(N))
ZADD key [NXXX] [CH] [INCR] score member [score member ...] 新增到有序set的一個或多個成員,或更新的分數,如果它已經存在 O(log(N))
ZCARD key 獲取一個排序的集合中的成員數量 O(1)
ZCOUNT key min max 返回分數範圍內的成員數量 O(log(N))
ZINCRBY key increment member 增量的一名成員在排序設定的評分 O(log(N))
ZINTERSTORE 相交多個排序集,導致排序的設定儲存在一個新的關鍵 O(NK)+O(Mlog(M))
ZLEXCOUNT key min max 返回成員之間的成員數量 O(log(N))
ZPOPMAX key [count] 刪除並返回排序集中得分最高的成員 O(log(N)*M)
ZPOPMIN key [count] 刪除並返回排序集中得分最低的成員 O(log(N)*M)
ZRANGE key start stop [WITHSCORES] 根據指定的index返回,返回sorted set的成員列表 O(log(N)+M)
ZRANGEBYLEX key min max [LIMIT offset count] 返回指定成員區間內的成員,按字典正序排列, 分數必須相同。 O(log(N)+M)
ZREVRANGEBYLEX key max min [LIMIT offset count] 返回指定成員區間內的成員,按字典倒序排列, 分數必須相同 O(log(N)+M)
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] 返回有序集合中指定分數區間內的成員,分數由低到高排序。 O(log(N)+M)
ZRANK key member 確定在排序集合成員的索引 O(log(N))
ZREM key member [member ...] 從排序的集合中刪除一個或多個成員 O(M*log(N))
ZREMRANGEBYLEX key min max 刪除名稱按字典由低到高排序成員之間所有成員。 O(log(N)+M)
ZREMRANGEBYRANK key start stop 在排序設定的所有成員在給定的索引中刪除 O(log(N)+M)
ZREMRANGEBYSCORE key min max 刪除一個排序的設定在給定的分數所有成員 O(log(N)+M)
ZREVRANGE key start stop [WITHSCORES] 在排序的設定返回的成員範圍,通過索引,下令從分數高到低 O(log(N)+M)
ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count] 返回有序集合中指定分數區間內的成員,分數由高到低排序。 O(log(N)+M)
ZREVRANK key member 確定指數在排序集的成員,下令從分數高到低 O(log(N))
ZSCORE key member 獲取成員在排序設定相關的比分 O(1)
ZUNIONSTORE 新增多個排序集和導致排序的設定儲存在一個新的關鍵 O(N)+O(M log(M))
ZSCAN key cursor [MATCH pattern] [COUNT count] 迭代sorted sets裡面的元素 O(1)

三、使用場景

3.1 排行榜系統

有序集合比較典型的使用場景就是排行榜系統。例如學生成績的排名。某視訊(部落格等)網站的使用者點贊、播放排名、電商系統中商品的銷量排名等。我們以部落格點贊為例。

  1. 新增使用者贊數

例如小編Tom發表了一篇博文,並且獲得了10個贊。

zadd user:ranking arcticle1 10
  1. 取消使用者贊數

這個時候有一個讀者又覺得Tom寫的不好,又取消了贊,此時需要將文章的贊數從榜單中減去1,可以使用zincrby。

zincrby user:ranking arcticle1 -1
  1. 檢視某篇文章的贊數
ZSCORE user:ranking arcticle1
  1. 展示獲取贊數最多的十篇文章

此功能使用zrevrange命令實現:

zrevrangebyrank user:ranking  0 9

3.2 電話號碼(姓名)排序

使用有序集合的ZRANGEBYLEX(點選可檢視該命令詳細說明)ZREVRANGEBYLEX可以幫助我們實現電話號碼或姓名的排序,我們以ZRANGEBYLEX為例
注意:不要在分數不一致的SortSet集合中去使用 ZRANGEBYLEX和 ZREVRANGEBYLEX 指令,因為獲取的結果會不準確。

  1. 電話號碼排序

我們可以將電話號碼儲存到SortSet中,然後根據需要來獲取號段:

redis> zadd phone 0 13100111100 0 13110114300 0 13132110901 
(integer) 3
redis> zadd phone 0 13200111100 0 13210414300 0 13252110901 
(integer) 3
redis> zadd phone 0 13300111100 0 13310414300 0 13352110901 
(integer) 3

獲取所有號碼:

redis> ZRANGEBYLEX phone - +
1) "13100111100"
2) "13110114300"
3) "13132110901"
4) "13200111100"
5) "13210414300"
6) "13252110901"
7) "13300111100"
8) "13310414300"
9) "13352110901"

獲取132號段:

redis> ZRANGEBYLEX phone [132 (133
1) "13200111100"
2) "13210414300"
3) "13252110901"

獲取132、133號段:

redis> ZRANGEBYLEX phone [132 (134
1) "13200111100"
2) "13210414300"
3) "13252110901"
4) "13300111100"
5) "13310414300"
6) "13352110901"
  1. 姓名排序

將名稱儲存到SortSet中:

redis> zadd names 0 Toumas 0 Jake 0 Bluetuo 0 Gaodeng 0 Aimini 0 Aidehua 
(integer) 6

獲取所有人的名字:

redis> ZRANGEBYLEX names - +
1) "Aidehua"
2) "Aimini"
3) "Bluetuo"
4) "Gaodeng"
5) "Jake"
6) "Toumas"

獲取名字中大寫字母A開頭的所有人:

redis> ZRANGEBYLEX names [A (B
1) "Aidehua"
2) "Aimini"

獲取名字中大寫字母C到Z的所有人:

redis> ZRANGEBYLEX names [C [Z
1) "Gaodeng"
2) "Jake"
3) "Toumas"

小結

本篇文章我們總結了Redis 有序集合物件的內部實現、常用命令以及常用的一些場景,有序集合提供了獲取指定分數和元素範圍查詢、計算成員排名等功能,合理的利用有序集合,能幫助我們在實際開發中解決很多問題。那麼大家在專案中對Redis有序集合物件的使用都有哪些場景呢,歡迎在評論區給我留言和分享,我會第一時間反饋!我們共同學習與進步!

參考

《Redis設計與實現》

《Redis開發與運維》

《Redis官方文件》

-----END-----

相關文章