有序集合型別 (Sorted Set或ZSet) 相比於集合型別多了一個排序屬性 score(分值),對於有序集合 ZSet 來說,每個儲存元素相當於有兩個值組成的,一個是有序結合的元素值,一個是排序值。有序集合保留了集合不能有重複成員的特性(分值可以重複),但不同的是,有序集合中的元素可以排序。
一、內部實現
有序集合是由 ziplist (壓縮列表) 或 skiplist (跳躍表) 組成的。
當資料比較少時,有序集合使用的是 ziplist 儲存的,有序集合使用 ziplist 格式儲存必須滿足以下兩個條件:
- 有序集合儲存的元素個數要小於 128 個;
- 有序集合儲存的所有元素成員的長度都必須小於 64 位元組。
如果不能滿足以上兩個條件中的任意一個,有序集合將會使用 skiplist 結構進行儲存。
有關ziplist 和skiplist 這兩種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 排行榜系統
有序集合比較典型的使用場景就是排行榜系統。例如學生成績的排名。某視訊(部落格等)網站的使用者點贊、播放排名、電商系統中商品的銷量排名等。我們以部落格點贊為例。
- 新增使用者贊數
例如小編Tom發表了一篇博文,並且獲得了10個贊。
zadd user:ranking arcticle1 10
- 取消使用者贊數
這個時候有一個讀者又覺得Tom寫的不好,又取消了贊,此時需要將文章的贊數從榜單中減去1,可以使用zincrby。
zincrby user:ranking arcticle1 -1
- 檢視某篇文章的贊數
ZSCORE user:ranking arcticle1
- 展示獲取贊數最多的十篇文章
此功能使用zrevrange命令實現:
zrevrangebyrank user:ranking 0 9
3.2 電話號碼(姓名)排序
使用有序集合的ZRANGEBYLEX(點選可檢視該命令詳細說明)或ZREVRANGEBYLEX可以幫助我們實現電話號碼或姓名的排序,我們以ZRANGEBYLEX為例
注意:不要在分數不一致的SortSet集合中去使用 ZRANGEBYLEX和 ZREVRANGEBYLEX 指令,因為獲取的結果會不準確。
- 電話號碼排序
我們可以將電話號碼儲存到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"
- 姓名排序
將名稱儲存到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官方文件》