需求
1、比赛记录分数,统计日榜和总榜
2、排行榜保留前3名
实现
一、引入redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
二、配置yml
spring:
redis:
host: localhost
port: 6379
三、初始化RedisTemplate
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
//连接工厂
redisTemplate.setConnectionFactory(redisConnectionFactory);
//设置序列化器
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
return redisTemplate;
}
}
四、创建redis工具类
@Component
public class RedisUtils {
@Resource
private RedisTemplate<String, String> redisTemplate;
// 相关方法会在后面说明
....
}
五、编写业务逻辑
1、每日新增排名
每日排名用日期作为key分割开
@Test
void addDayData() {
// 添加第一天数据
String key1 = "score:rank:20220620";
redisUtils.zAdd(key1, "jack", 100);
redisUtils.zAdd(key1, "david", 150);
redisUtils.zAdd(key1, "chris", 76);
redisUtils.zAdd(key1, "soho", 200);
// 添加第二天数据,其中david两天都有参赛,并且第二天成绩比第一天好
String key2 = "score:rank:20220621";
redisUtils.zAdd(key2, "justin", 30);
redisUtils.zAdd(key2, "david", 173);
redisUtils.zAdd(key2, "luke", 89);
redisUtils.zAdd(key2, "gk", 190);
}
redisUtils.zAdd()方法的实现如下:
/**
* 向zset中添加分数
* @param key key
* @param value 存放唯一标识
* @param score 分数
* @return boolean
*/
public Boolean zAdd(String key, String value, double score) {
return redisTemplate.opsForZSet().add(key, value, score);
}
2、获取日榜前三数据
@Test
void getDayRank() {
// 获取日榜前三,假如今天是20220621
String key = "score:rank:20220621";
redisUtils.zReverseRangeWithScores(key, 0, 2)
.forEach(item ->
System.out.printf("user: %s, score: %s%n", item.getValue(), item.getScore()));
}
redisUtils.zReverseRangeWithScores()方法的实现如下:
/**
* 从大到小截取数据
* @param key key
* @param start 从哪里开始(0表示第一名)
* @param end 截取到哪(9表示截取10名)
* @return Set
*/
public Set<ZSetOperations.TypedTuple<String>> zReverseRangeWithScores(String key, long start, long end) {
return redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);
}
2022年6月21日的排名如下:
user: gk, score: 190.0
user: david, score: 173.0
user: luke, score: 89.0
3、获取总榜前三数据
@Test
void getTotalRank() {
// 获取所有日榜的keys
Set<String> listKey = redisUtils.getListKey("score:rank");
String key = "score:rank:20220621";
String destKey = "score:rank:total";
// 先查缓存
Set<ZSetOperations.TypedTuple<String>> typedTuples = redisUtils.zReverseRangeWithScores(destKey, 0, 2);
if (typedTuples.isEmpty()) {
// 没有缓存的话将日榜并集,并将结果存储到新key,并设置有效期为15分钟
redisUtils.zUnionAndStore(key, listKey, destKey);
redisUtils.expire(destKey, 15L, TimeUnit.MINUTES);
typedTuples = redisUtils.zReverseRangeWithScores(destKey, 0, 2);
}
// 获取总榜前10
typedTuples.forEach(item ->
System.out.printf("user: %s, score: %s%n", item.getValue(), item.getScore()));
}
2022年6月21日的总排名如下:
user: soho, score: 200.0
user: gk, score: 190.0
user: david, score: 173.0
redisUtils.zUnionAndStore() 和 redisUtils.expire() 的实现如下:
/**
* 并集所有keys
*
* RedisZSetCommands.Aggregate.MAX -> 保留最大
* RedisZSetCommands.Aggregate.MIN -> 保留最小
* RedisZSetCommands.Aggregate.SUM -> 相加(默认)
*
* @param key 要被并集的key
* @param otherKeys 所有要并集的key
* @param destKey 新key
* @return 新set的长度
*/
public Long zUnionAndStore(String key, Collection<String> otherKeys, String destKey) {
return redisTemplate.opsForZSet().unionAndStore(key, otherKeys, destKey, RedisZSetCommands.Aggregate.MAX);
}
/**
* 设置key的过期时间
* @param key 目标key
* @param timeout 时间
* @param timeUnit 单位
* @return boolean
*/
public Boolean expire(String key, long timeout, TimeUnit timeUnit) {
return redisTemplate.expire(key, timeout, timeUnit);
}