- 数据存储规划
在排行榜中,每个用户都会有一个唯一的标识(ID),同时需要记录该用户的积分(Score)。因此,我们可以将每个用户的ID作为有序集合中的一个元素,将用户的积分作为该元素的分数。
ZADD rank 1000 user1
ZADD rank 2000 user2
ZADD rank 3000 user3
上面的命令向名为rank的有序集合中添加了3个成员,分别是user1、user2和user3,他们的积分分别是1000、2000和3000。
- 排行查询操作
使用Redis的ZREVRANGE命令,可以按照分数从高到低的顺序返回指定数量的元素,也就是排行榜中前N名的用户。
ZREVRANGE rank 0 2 WITHSCORES
上面的命令表示返回有序集合rank中排名前3的用户,同时返回他们对应的积分。执行该命令后,会返回以下结果:
1) "user3"
2) "3000"
3) "user2"
4) "2000"
5) "user1"
6) "1000"
其中,每个元素的前一个返回值是用户的ID,后一个返回值是用户的积分。
如果需要查询某个用户的排行信息,可以使用Redis的ZREVRANK命令,通过用户ID获取该用户的排名。注意,由于排行榜是从1开始计算的,而有序集合是从0开始计算的,因此需要将计算结果加1才能得到正确的排名。
ZREVRANK rank user2
上面的命令表示查询用户user2在排行榜中的排名。执行该命令后,会返回以下结果:
(integer) 1
- 排行更新操作
使用Redis的ZINCRBY命令,可以增加指定用户的积分。如果用户不存在,该命令会自动创建一个新成员并设置初始积分为0,然后再增加积分。
ZINCRBY rank 1000 user1
上面的命令表示将用户user1的积分增加1000。执行该命令后,用户user1的积分变成了2000。
- 删减排行榜
使用Redis的ZREMRANGEBYRANK命令,可以删除有序集合中指定排名范围的成员。例如,可以删除排行榜中前1000名之外的所有用户。
ZREMRANGEBYRANK rank 0 -1001
上面的命令表示删除有序集合rank中除了前1000名之外的所有成员。执行该命令后,排行榜中只剩下前1000名用户。
- 排行榜过期时间设置
如果需要自动清除某个时间段内的排行榜,可以使用Redis的EXPIRE命令为有序集合设置过期时间。
EXPIRE rank 3600
上面的命令表示为有序集合rank设置一个过期时间为3600秒(即1小时)
Java 实现:
import redis.clients.jedis.Jedis;
import java.util.Map;
import java.util.LinkedHashMap;
import java.util.List;
public class RankList {
private static final String REDIS_HOST = "localhost";
private static final int REDIS_PORT = 6379;
private static final String RANK_KEY = "rank";
public static void main(String[] args) {
// 初始化Jedis客户端
Jedis jedis = new Jedis(REDIS_HOST, REDIS_PORT);
// 添加用户信息
addUser(jedis, "user1", 1000);
addUser(jedis, "user2", 2000);
addUser(jedis, "user3", 3000);
// 查询排行榜前3名
List<Map.Entry<String, Double>> topUsers = getTopUsers(jedis, 3);
System.out.println("Rank\tUser ID\tScore");
for (int i = 0; i < topUsers.size(); i++) {
Map.Entry<String, Double> entry = topUsers.get(i);
System.out.printf("%d\t%s\t%.0f\n", i + 1, entry.getKey(), entry.getValue());
}
// 更新用户积分
incrementScore(jedis, "user1", 1000);
// 查询指定用户的排名信息
long rank = getUserRank(jedis, "user1");
System.out.printf("User1's rank: %d\n", rank);
// 删除排行榜前1000名之外的用户
removeExtraUsers(jedis, 1000);
// 关闭Jedis客户端
jedis.close();
}
// 添加用户信息
public static void addUser(Jedis jedis, String userId, double score) {
jedis.zadd(RANK_KEY, score, userId);
}
// 查询排行榜前N名用户
public static List<Map.Entry<String, Double>> getTopUsers(Jedis jedis, int limit) {
return new LinkedHashMap<>(jedis.zrevrangeWithScores(RANK_KEY, 0, limit - 1)).entrySet().stream()
.sorted(Map.Entry.<String, Double>comparingByValue().reversed())
.toList();
}
// 更新用户积分
public static void incrementScore(Jedis jedis, String userId, double increment) {
jedis.zincrby(RANK_KEY, increment, userId);
}
// 查询指定用户的排名
public static long getUserRank(Jedis jedis, String userId) {
return jedis.zrevrank(RANK_KEY, userId);
}
// 删除排行榜前N名之外的用户
public static void removeExtraUsers(Jedis jedis, int limit) {
long rank = jedis.zcard(RANK_KEY) - limit;
if (rank > 0) {
jedis.zremrangeByRank(RANK_KEY, 0, rank - 1);
}
}
}
上面的代码中,首先定义了Redis服务器的地址和端口号等常量。然后通过Jedis客户端连接到Redis服务器,在Redis中添加用户信息、查询排行榜等操作。其中,addUser()方法用于添加用户信息,getTopUsers()方法用于查询排行榜前N名用户,incrementScore()方法用于更新用户积分,getUserRank()方法用于查询指定用户的排名,removeExtraUsers()方法用于删除排行榜中前N名之外的用户。
请注意,在上面的代码示例中,为了保证排行榜中元素的有序性,使用了LinkedHashMap来保存排行榜前N名用户的信息,并使用Java 16中的toList()方法将元素转换为列表形式输出。如果使用的是较早版本的Java,可以自行实现一个按照值排序的Comparator类,或者使用其他第三方库(如Guava等)来进行列表操作。