Redis 有序集合实现排行榜(结合springboot实现)
Redis 有序集合(sorted set)和集合一样也是 string 类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。正是因为有分值,所以很适合用在排行榜业务中。下面举例对redis有序集合实现排行榜功能进行说明。比如,业务需要某用户近
一、Redis 有序集合简介
Redis 有序集合(sorted set)和集合一样也是 string 类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。正是因为有分值,所以很适合用在排行榜业务中。
二、具体业务实现举例
下面举例对redis有序集合实现排行榜功能进行说明。比如,业务需要某用户近30日工具使用率的排行榜。
(1)为每个用户建立有序集合记录每日各个工具的使用频次,keyPrefix+userId+day作为有序集合的key。例如toolRank:11223344:2022-10-14,该key的有效期为30天,即最多只保存用户一个月的使用情况;
(2)用户每次打开工具即将有序集合中该工具的score+1;
(3)取当前日期前30天的全部集合,取集合间的并集,其中相同元素的score累加,形成的新集合即为用户近30日的使用率排行榜,即将日榜合并为月榜;
三、代码实现
springboot代码实现如下:
RedisServiceImpl.java
//=============================== sort set =================================
/**
* 添加指定元素到有序集合中
* @param key
* @param score
* @param value
* @return
*/
@Override
public boolean sortSetAdd(String key, String value, double score) {
return redisTemplate.opsForZSet().add(key, value, score);
}
/**
* 有序集合中对指定成员的分数加上增量 increment
* @param key
* @param value
* @param i
* @return
*/
@Override
public double sortSetZincrby(String key, String value, double i) {
//返回新增元素后的分数
return redisTemplate.opsForZSet().incrementScore(key, value, i);
}
/**
* 向zset结构中指定元素的分数加上增量 i,新增key设置有效期
* @param key
* @param value
* @param i
* @param time
* @param unit
* @return
*/
@Override
public double sortSetZincrby(String key, String value, double i, Long time, TimeUnit unit) {
long members = redisTemplate.opsForZSet().zCard(key);
//返回新增元素后的分数
double sorce = redisTemplate.opsForZSet().incrementScore(key, value, i);
//新增key则设置有效期
if (0 == members && null != time) {
redisTemplate.expire(key, time, unit);
}
return sorce;
}
/**
* 获得有序集合指定范围元素 (从大到小)
* @param key
* @param start
* @param end
* @return
*/
@Override
public Set<Object> sortSetReverseRange(String key, int start, int end) {
return redisTemplate.opsForZSet().reverseRange(key, start, end);
}
/**
* 将有序集合oZset和oZsetList的交集合并为nZset
*
* @param oZset
* @param oZsetList
* @param nZset
*/
@Override
public Long sortSetIntersection(String oZset, List<String> oZsetList,
String nZset) {
Long size = redisTemplate.opsForZSet().intersectAndStore(oZset, oZsetList, nZset,
Aggregate.SUM, null);
return size;
}
/**
* 将有序集合oZset和oZsetList的并集合并为nZset,每个集合的权重相同
*
* @param oZset
* @param oZsetList
* @param nZset
*/
@Override
public Long sortSetUnionAndStore(String oZset, List<String> oZsetList,
String nZset) {
int[] weights = new int[oZsetList.size() + 1];
for (int i = 0; i <= oZsetList.size(); ++i) {
weights[i] = 1;
}
Long size = redisTemplate.opsForZSet().unionAndStore(oZset, oZsetList, nZset,
Aggregate.SUM, Weights.of(weights));
return size;
}
/**
* 从zset结构中移除元素
*/
@Override
public Long sortSetRemove(String key, Object... values) {
return redisTemplate.opsForZSet().remove(key, values);
}
RedisController.java
@Autowired
private RedisService redisService;
private static final String keyPrefix = "toolRang:";
@ApiOperation(value = "用户访问工具")
@RequestMapping(value = "/openTool", method = RequestMethod.POST)
@ResponseBody
public CommonResult openTool(@RequestParam("userId") String userId,
@RequestParam(value = "toolId") String toolId,
@RequestParam(value = "day", required = false) String day) {
if (Objects.isNull(day)) {
day = LocalDate.now().format(DateTimeFormatter.ISO_DATE);
}
String key = keyPrefix.concat(userId).concat(":") + day;
//搜索次数 + 1
redisService.sortSetZincrby(key, toolId, 1, 30L, TimeUnit.DAYS);
return CommonResult.success(null);
}
@ApiOperation(value = "获取排行榜")
@RequestMapping(value = "/getRang", method = RequestMethod.POST)
@ResponseBody
public CommonResult<Set<Object>> getToolRang(@RequestParam("userId") String userId,
@RequestParam(value = "day", required = false) String day) {
String key = keyPrefix.concat(userId).concat(":");
List<String> otherKeys = new ArrayList<>();
LocalDate currentDay = LocalDate.now();
if (!Objects.isNull(day)) {
currentDay = LocalDate.parse(day, DateTimeFormatter.ISO_DATE);
}
for (int i = -29; i < 0; i++) {
LocalDate otherDay = currentDay.plusDays(i);
otherKeys.add(key + otherDay.format(DateTimeFormatter.ISO_DATE));
}
String currentKey = key + currentDay.format(DateTimeFormatter.ISO_DATE);
String unionKey = keyPrefix.concat(userId).concat(":") + System.currentTimeMillis();
redisService.sortSetUnionAndStore(currentKey, otherKeys, unionKey);
Set<Object> res = redisService.sortSetReverseRange(unionKey, 0, -1);
redisService.del(unionKey);
return CommonResult.success(res);
}
@ApiOperation(value = "删除排行榜")
@RequestMapping(value = "/delRang", method = RequestMethod.POST)
@ResponseBody
public CommonResult<Set<Object>> delToolRang(@RequestParam("userId") String userId,
@RequestParam(value = "day", required = false) String day) {
String key = keyPrefix.concat(userId);
if (!Objects.isNull(day)) {
key = key.concat(":").concat(day);
}
redisService.delByPattern(key.concat("*"));
return CommonResult.success(null);
}
更多推荐
所有评论(0)