Redis 實現(xiàn)排行榜,看這篇文章就夠了
前言
在日常的服務器開發(fā)過程中,經常會遇到排行榜的需求。這里我們主要討論使用redis zset來實現(xiàn)一般的常用排行榜。也就是單key排行榜,分數(shù)為整數(shù),一般長度不過萬。
ZSet概述
Redis的ZSet(Sorted Set)是一種有序集合,它結合了Set和SortedList的特性,成員是唯一的,且按照成員的分數(shù)(score)進行從小到大排序。在ZSet中,每個元素都會關聯(lián)一個分數(shù),分數(shù)可以重復,但元素不能重復。這使得ZSet非常適合用于實現(xiàn)排行榜等場景。
每個直播間都有粉絲的排行榜,可以通過key+直播間id來作為redis的key。例如broadcast:20210108231。每個直播間的觀眾按照點贊數(shù)排序。則觀眾剛剛進入直播間即可通過ZADD添加排行榜。
張三 觀眾進入直播間。
李四 進入直播間
ZADD [key] [score] [value]
ZADD broadcast:20240108231 1 zhangsan ZADD broadcast:20240108231 1 lisi
1、加分值
`李四` 送了直播間兩顆小紅心。李四分值`加2`。
`ZINCRBY [key] increment [member]
ZINCRBY broadcast:20240108231 2 lisi
2、展示榜單
通過如上指令對直播間分值進行設置之后,得到redis的value如下:
127.0.0.1:6379> ZRANGE broadcast:20240108231 0 -1 WITHSCORES
1) "zhaoliu"
2) "2"
3) "wangwu"
4) "5"
5) "lisi"
6) "8"
7) "zhangsan"
8) "10"
3、查看直播間人數(shù)
ZCARD key 返回集合數(shù)量。
127.0.0.1:6379> zcard broadcast:20240108231
(integer) 4
4、離開直播間
張三離開直播間,則刪除對應key。ZREM [key] [value]
127.0.0.1:6379> ZREM broadcast:20240108231 zhangsan
(integer) 1
127.0.0.1:6379> ZREVRANGE broadcast:20240108231 0 2
1) "lisi"
2) "wangwu"
3) "zhaoliu"
5、周榜
真實場景中肯定會有時間段的劃分,例如查看日榜、周榜、月榜。只需要按照最小的單位按照時間區(qū)分成不同的集合,最后求出這些集合的并集即可。
圖片
ZADD broadcast:1 10 zhangsan
ZADD broadcast:1 10 lisi
ZADD broadcast:2 5 zhangsan
ZADD broadcast:2 5 wangwu
如此,得到兩個zset集合,最后得到兩個集合的并集。
ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
WEIGHTS 可以設置每個集合的權重,意為在原來集合分數(shù)乘權重得到輸出集合的值。兩個集合并集時,如果有相同的key,則可以通過 SUM|MIN|MAX 進行控制。默認為SUM。
127.0.0.1:6379> ZUNIONSTORE broadcast:week:1 2 broadcast:1 broadcast:2
(integer) 3
127.0.0.1:6379> ZRANGE broadcast:week:1 0 -1 WITHSCORES
1) "wangwu"
2) "5"
3) "lisi"
4) "10"
5) "zhangsan"
6) "15"
Java實現(xiàn)排行榜
要實現(xiàn)一個排行榜,我們需要做以下幾件事:
- 創(chuàng)建一個ZSet,其中包含用戶ID和對應的分數(shù)。
- 獲取排行榜數(shù)據(jù),并按照分數(shù)降序排列。
以下是一個簡單的Java代碼示例,展示了如何使用Jedis庫來實現(xiàn)基于Redis的ZSet的秒級排行榜
public class RedisZSetRank{
@Autowired
private RedisTemplate redisTemplate;
// 添加用戶分數(shù)到排行榜
public void addScore(String userId, double score) {
redisTemplate.opsForZSet().add("ranking",userId,score);
}
// 獲取排行榜數(shù)據(jù)
public Set<String> getRanking() {
// 獲取前10名
return redisTemplate.opsForZSet().reverseRange("ranking", 0, 10);
}
// 查看某條記錄的排名
public Long ranking(String code) {
Long ranking = redisTemplate.opsForZSet().rank("ranking", code);
if (ranking != null) {
//有序集合的索引是從0開始的,所以查詢出來的排名要+1。
return ranking + 1;
}
return ranking;
}
public static void main(String[] args) {
// 添加用戶分數(shù)
rank.addScore("user1", 100);
rank.addScore("user2", 200);
// 獲取排行榜數(shù)據(jù)
Set<String> rankingData = rank.getRanking();
for (String userId : rankingData) {
System.out.println("User ID: " + userId);
}
}
}
這樣就實現(xiàn)了簡單的排行榜。
總結
Redis 的有序集合 Sorted Set中的成員是唯一的,但分數(shù)(score)卻可以重復,這一點恰恰可以被我們用來實現(xiàn)排行榜的功能。