一. 前言
热搜排行榜实现要点: 性能/简单/时效 .
redis性能高是公认的, redis的数据结构zset有个score,可以实现排序,redis可以给key设定时效
历史搜索的实现也差不多,可以用redis的数据结构list(列表)实现,每次在头部插入数据,然后控制列表的长度就行
二. 示例
2.1 热搜排行榜-工具类
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import java.io.Serializable;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 排行榜工具
*
* @author 任未然
* @date 2021/12/25
*/
public class LeaderboardUtil implements Serializable {
/**
* redis
*/
private static StringRedisTemplate redisTemplate;
public static StringRedisTemplate getRedisTemplate(){
if(null == redisTemplate){
synchronized (LeaderboardUtil.class){
redisTemplate = SpringContextUtil.getBean(StringRedisTemplate.class);
return redisTemplate;
}
}
return redisTemplate;
}
/**
* 排行榜添加或更新数据score
* 排行版每天凌晨刷新一遍
* @param key
* @param member
*/
public static void leaderboardAdd(String key, String member){
// 添加或更新数据
getRedisTemplate().opsForZSet().incrementScore(key, member, 1);
// 设置有效期
LocalDateTime localDateTime = LocalDateTime.of(LocalDate.now(), LocalTime.of(23, 59, 59));
LocalDateTime now = LocalDateTime.now();
// 时间差
Duration between = Duration.between(now, localDateTime);
getRedisTemplate().expire(key,between);
}
/**
* 通过索引区间返回有序集合成指定区间内的成员 分数从高到低
*
* @param key 键
* @param size 获取个数
* @return 成员集合
*/
public static List<LeaderboardDto> leaderboardGet(String key, int size) {
List<LeaderboardDto> leaderboardDtos = null;
try {
Set<ZSetOperations.TypedTuple<String>> typedTuples = getRedisTemplate().opsForZSet().reverseRangeWithScores(key, 0, -1);
if(CollectionUtils.isNotEmpty(typedTuples)){
leaderboardDtos = typedTuples.stream().limit(size).map(stringTypedTuple -> {
return LeaderboardDto.builder()
.value(stringTypedTuple.getValue())
.score(stringTypedTuple.getScore())
.build();
}).collect(Collectors.toList());
}
} catch (Exception e) {
e.printStackTrace();
}
return leaderboardDtos;
}
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
class LeaderboardDto implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("值")
private String value;
@ApiModelProperty("分数")
private Double score;
}
2.2 历史搜索-工具类
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.io.Serializable;
import java.util.List;
/**
* 历史搜索工具类
*
* @author 任未然
* @date 2021/12/25
*/
public class HistoricalSearchUtil implements Serializable {
/**
* 列表最大长度
*/
public static final int max_size = 10;
/**
* redis
*/
private static ListOperations<String,String> redisTemplate;
public static ListOperations<String,String> getRedisTemplate(){
if(null == redisTemplate){
synchronized (LeaderboardUtil.class){
redisTemplate = SpringContextUtil.getBean(StringRedisTemplate.class).opsForList();
return redisTemplate;
}
}
return redisTemplate;
}
/**
* 添加记录
*
* @param key 键
* @param value 值
* @return boolean
*/
public static void lpush(String key, String value) {
try {
ListOperations<String, String> redisTemplate = getRedisTemplate();
redisTemplate.leftPush(key, value);
// 截取列表长度
redisTemplate.trim(key,0,max_size-1);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取列表数据
*
* @param key 键
* @return 元素列表
*/
public static List<String> lrange(String key) {
try {
ListOperations<String, String> redisTemplate = getRedisTemplate();
return redisTemplate.range(key, 0, -1);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
网友评论