前言
在日常的服务器开发过程中,经常会遇到排行榜的需求。这里我们主要讨论使用redis zset来实现一般的常用排行榜。也就是单key排行榜,分数为整数,一般长度不过万。
ZSet概述
Redis的ZSet(Sorted Set)是一种有序集合,它结合了Set和SortedList的特性,成员是唯一的,且按照成员的分数(score)进行从小到大排序。在ZSet中,每个元素都会关联一个分数,分数可以重复,但元素不能重复。这使得ZSet非常适合用于实现排行榜等场景。
每个直播间都有粉丝的排行榜,可以通过key+直播间id来作为redis的key。例如broadcast:20210108231。每个直播间的观众按照点赞数排序。则观众刚刚进入直播间即可通过ZADD添加排行榜。
张三观众进入直播间。
李四进入直播间
ZADD [key] [score] [value]
ZADD broadcast:20240108231 1 zhangsan
ZADD broadcast:20240108231 1 lisi
2、加分值
`李四`送了直播间两颗小红心。李四分值`加2`。
`ZINCRBY [key] increment [member]
ZINCRBY broadcast:20240108231 2 lisi
3、展示榜单
通过如上指令对直播间分值进行设置之后,得到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"
4、查看直播间人数
ZCARD key
返回集合数量。
127.0.0.1:6379> zcard broadcast:20240108231
(integer) 4
5、离开直播间
张三离开直播间,则删除对应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"
6、周榜
真实场景中肯定会有时间段的划分,例如查看日榜、周榜、月榜。只需要按照最小的单位按照时间区分成不同的集合,最后求出这些集合的并集即可。
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]
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"
WEIGHTS
可以设置每个集合的权重,意为在原来集合分数乘权重得到输出集合的值。
两个集合并集时,如果有相同的key,则可以通过 SUM|MIN|MAX
进行控制。默认为SUM
。
JAVA实现排行榜
要实现一个排行榜,我们需要做以下几件事:
- 创建一个ZSet,其中包含用户ID和对应的分数。
- 获取排行榜数据,并按照分数降序排列。
以下是一个简单的Java代码示例,展示了如何使用Jedis库来实现基于Redis的ZSet的秒级排行榜
public class RedisZSetRank{
@Autowired
private RedisTemplate redisTemplate;
// 添加用户分数到排行榜
public void addScore(String userId, double score) {
redisTemplate.opsForZSet().add("ranking",userId,score);
}
// 获取排行榜数据
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) {
// 添加用户分数
rank.addScore("user1", 100);
rank.addScore("user2", 200);
// 获取排行榜数据
Set<String> rankingData = rank.getRanking();
for (String userId : rankingData) {
System.out.println("User ID: " + userId);
}
}
}
这样就实现了简单的排行榜。
总结
Redis 的有序集合 Sorted Set中的成员是唯一的,但分数(score)却可以重复,这一点恰恰可以被我们用来实现排行榜的功能。