redis 有序集合小試

2021-08-07 13:53:16 字數 2892 閱讀 2990

假定乙個使用場景:

排行榜。

選手報名參加活動,觀眾可以對選手進行投票,每個觀眾對同一名選手只能投一票,活動期間最多投四票。後台需要提供如下介面:

首先需要一張表儲存投票記錄,一次投票就是一條記錄。這張表相當於投票明細,判斷每人只投一張票以及最多投四票都依賴對這張表的查詢。

如果直接對這張表做top 10的查詢,則需要根據選手id做聚合查詢,這樣每次查詢必然耗時。為了優化查詢,可以增加另一張排行榜表,用乙個定時任務每隔一段時間對原表做聚合查詢,然後將結果寫進排行榜表裡,表裡包含投票數及排名的字段,這樣查詢top 10和排名的時候直接查這張表。引入另一張表加快了效能,但犧牲了實時性,活動說明裡需加上類似「榜單資料每10分鐘同步一次」的話來告知使用者。

對於排行榜的需求,redis有乙個資料結構非常適合做這件事,那就是有序集合(sorted set)。

redis的有序集合相關命令

有序集合和集合一樣可以儲存字串,另外有序集合的成員可以關聯乙個分數(score),這個分數用於集合排序。下面以投票為例說明常見的命令,vote是有序集合的key。

投票操作如下:

命令格式:

zincrby key increment member

127.0.0.1:6379> zincrby vote 1 xiaodu

"1"127.0.0.1:6379> zincrby vote 1 wangke

"1"127.0.0.1:6379> zincrby vote 1 wanghaolong

"1"然後集合中每個成員的值都是1

現在來看某乙個成員的分數及排名

分數命令:

命令格式:zscore key member

127.0.0.1:6379> zscore vote xiaodu

"1"檢視他的排名

命令格式:127.0.0.1:6379> zrevrank vote xiaodu

(integer) 0

可以檢視出在這個vote的集合中xiaodu排名為0,因為有序集合排序從0開始,0即為排名第一。

然後給wanghaolong這個成員分數加3

zincrby vote 3 wanghaolong

再來看小杜的排名

zrevrank vote xiaodu

「1」然後看王浩龍的排名:

127.0.0.1:6379> zrevrank vote wanghaolong

(integer) 0

由此就能看出有序集合以分數高低來進行從高到低的排序。

獲取前十名的排序

127.0.0.1:6379> zrevrange vote 0 9   從高到低

1) "wanghaolong"

2) "xiaodu"

3) "wangke"

4) "b"

5) "a"

其中成員a與成員b的分數都是1,b插入在a之前,由此得出如果分數相同那麼插入順序決定排序。

獲取前十名以及她們的分數:從高到低

127.0.0.1:6379> zrevrange vote 0 9 withscores

1) "wanghaolong"

2) "4"

3) "xiaodu"

4) "2"

5) "wangke"

6) "1"

7) "b"

8) "1"

9) "a"

10) "1"

獲取參與的成員數:

127.0.0.1:6379> zcard vote

(integer) 5

127.0.0.1:6379>

回到業務需求上:

大部分需求都已經得到滿足,還剩下兩個資料需要單獨說一下。介面2中的總投票數沒有直接的介面獲得,一種方法是先用zrange遍歷所有的key,然後對score進行求和,另一種方法是對總票數單獨用乙個資料結構儲存。介面3的距離上一名差的票數,先用zrevrank獲取自己排名,然後用zrevrange獲取上一排名的分數,最後用自己的分數減去上一名的分數即可,**示例python**如下:php邏輯一樣

def

get_next_step

(redis_key, member):

next_step = none

score = redis.zscore(redis_key, member)

rank = redis.zrevrank(redis_key, member)

if rank > 0:

next_member = redis.zrevrange(redis_key, rank - 1, rank - 1, withscores=true)

next_step = next_member[0][1] - score

return next_step

另外如果兩個key的score相同,排序邏輯是按照插入順序排序。在有些情況下這個可能不滿足實際要求,因此需要按實際情況重新設計key。比如如果要求同分數情況下按時間排序,那麼key最好加上時間戳字首。

redis與資料庫的同步

redis通常是作為快取層加速查詢的,如果資料沒有做持久化則有概率會丟失資料。乙個方案是用定時任務定時同步redis與資料庫的資料,資料庫裡儲存著原始資料,通過計算資料庫的資料和redis做對比,可以修正由於redis不穩定導致的資料不一致。這裡需要注意的是在同步過程時redis的資料有可能還在增長,因此最好先讀redis的資料,然後記下時間,查詢指定時間段裡的資料庫的資料,最後再用zincrby增量修正redis資料,而不是直接用zadd覆蓋redis資料。

redis的有序集合是乙個非常高效的資料結構,可以替代資料庫裡一些很難實現的操作。它的乙個典型應用場景就是排行榜,通過zrank可以快速得到使用者的排名,通過zrange可以快速得到top n的使用者列表,它們的複雜度都是o(log(n)),用來替代資料庫查詢可以大大提公升效能。

Redis 有序集合

redis 有序集合和集合一樣也是string型別元素的集合,且不允許重複的成員。不同的是每個元素都會關聯乙個double型別的分數。redis正是通過分數來為集合中的成員進行從小到大的排序。有序集合的成員是唯一的,但分數 score 卻可以重複。集合是通過雜湊表實現的,所以新增,刪除,查詢的複雜度...

Redis有序集合

redis 有序集合和集合一樣也是string型別元素的集合,且不允許重複的成員。不同的是每個元素都會關聯乙個double型別的分數。redis正是通過分數來為集合中的成員進行從小到大的排序。有序集合的成員是唯一的,但分數 score 卻可以重複。集合是通過雜湊表實現的,所以新增,刪除,查詢的複雜度...

redis 有序集合

import redis pool redis.connectionpool host 127.0.0.1 實現乙個連線池 r redis.redis connection pool pool for i in range 100 r.zadd 1 i,i 表名稱為1,新增內容為 1到100,對應分...