redis排行榜相關

2021-08-15 19:12:59 字數 2722 閱讀 5543

排行榜是業務開發中常見的乙個場景,如何設計乙個好的資料結構能夠滿足高效實時的查詢,下面我們結合乙個實際例子來討論一下。

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

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

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

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

redis的有序集合相關命令

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

#給alice投票

redis> zincrby vote_activity 1 alice

"1"#給bob投票

redis> zincrby vote_activity 1 bob

"1"#給alice投票

redis> zincrby vote_activity 1 alice

"2"#檢視alice投票數

redis> zscore vote_activity alice

"2"#獲取alice排名(從高到低,zero-based)

redis> zrevrank vote_activity alice

(integer) 0

#獲取前10名(從高到低)

redis> zrevrange vote_activity 0 9

1) "alice"

2) "bob"

#獲取前10名及對應的分數(從高到低)

redis> zrevrange vote_activity 0 9 withscores

1) "alice"

2) "2"

3) "bob"

4) "1"

#獲取總參與選手數

redis> zcard vote_activity

(integer) 2

介面實現

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

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。比如如果要求同分數情況下按時間排序,那麼key最好加上時間戳字首。

redis與資料庫的同步

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

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

我們來**下它支援的功能, 首先當然就是支援按分值排序的功能. 由此可以猜測它底層是按score為key, name為value的tree結構(因為支援範圍查詢, 以及按分值排序). 但是該有序集又支援按name來修改score. 這樣需求下, 又演變成name為key, score為value的map結構了. 單獨的一種資料結構, 無法滿足其需求, 兩個都不可或缺. 那答案究竟是什麼? 

redis原始碼的定義如下:

typedef struct zset zset;

這樣就比較清晰了, 它採用了復合結構, 字典維護了name=>score的對映表, 而跳躍表則維護了按score排序的列表. 按name和按score的範圍查詢都天然支援. 

具體的解讀,可參考<

redis 排行榜應用

我分享這個主要是因為 redis 的排行榜思路很讚 排行榜功能是乙個很普遍的需求。使用 redis 中有序集合的特性來實現排行榜是又好又快的選擇。一般排行榜都是有實效性的,比如 使用者積分榜 如果沒有實效性一直按照總榜來排,可能榜首總是幾個老使用者,對於新使用者來說,那真是太令人沮喪了。首先,來個 ...

redis排行榜例子

1 儲存幾個排行榜成員資料 這裡可以理解為把自己系統已有資料載入到 redis 中 zadd testtop 23 member1 25 member2 2 增加某個人的分數 這裡的分數就是排行的依據可以是浮點型別 zincrby testtop 20 member1 此時 testtop 中 me...

redis實現排行榜

排行榜功能是乙個很普遍的需求。設想在乙個遊戲中,有上百萬的玩家資料,如果現在需要你根據玩家的經驗值整理乙個前20名的排行榜,你會怎麼做呢?你不可能 order by limit 去實現 select from game socre order by score desc limit 0,20 使用 ...