假定乙個使用場景:
排行榜。
選手報名參加活動,觀眾可以對選手進行投票,每個觀眾對同一名選手只能投一票,活動期間最多投四票。後台需要提供如下介面:
首先需要一張表儲存投票記錄,一次投票就是一條記錄。這張表相當於投票明細,判斷每人只投一張票以及最多投四票都依賴對這張表的查詢。
如果直接對這張表做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,對應分...