1.使用场景
现在公司有个项目,类似于今日头条,需要实现对应分类阅读排行榜的功能。
每一篇文章所属于一个分类,当用户阅读该文章时,阅读次数+1,排行榜实时变化。
2.redis的ZSet数据结构
zset为有序集合。就是在set的基础上,添加了一个score值。zset的每一个成员都有一个分数与之对应,并且分数可以重复。score就相当于权重,可以根据score值进行排序展示。
项目中使用了SpringBoot整合的redisTemplate
常用方法如下:
-
Boolean add(K key, V value, double score):新增一个有序集合,存在的话为false,不存在的话为true
-
Long add(K key, Set<TypedTuple<V>> tuples):新增一个有序集合
-
Long remove(K key, Object... values):从有序集合中移除一个或者多个元素
-
Double incrementScore(K key, V value, double delta):增加元素的score值,并返回增加后的值
-
Set<V> range(K key, long start, long end):通过索引区间返回有序集合成指定区间内的成员,其中有序集成员按分数值递增(从小到大)顺序排列
-
Set<TypedTuple<V>> rangeWithScores(K key, long start, long end):通过索引区间返回有序集合成指定区间内的成员对象,其中有序集成员按分数值递增(从小到大)顺序排列
-
Set<V> rangeByScore(K key, double min, double max):通过分数返回有序集合指定区间内的成员,其中有序集成员按分数值递增(从小到大)顺序排列
-
Set<V> reverseRange(K key, long start, long end):通过索引区间返回有序集合成指定区间内的成员,其中有序集成员按分数值递减(从大到小)顺序排列
-
Double score(K key, Object o):获取指定成员的score值
3.代码实例
(1)项目中的缓存常量类
CacheConstant.java
/**
* 分类研报购买次数
*/
public static final String REPORT_CATEGORY_BUY_COUNT = "report_report_category_buy_count:";
(2)CacheService.java
@Autowired
private RedisTemplate<String, Object> template;
/**
* 增加研报分类阅读次数(查看文章详情接口时调用)
*
* @param categoryId
*/
public void incrReportCategoryReadCount(Long categoryId) {
template.opsForZSet().incrementScore(CacheConstant.REPORT_CATEGORY_READ_COUNT, categoryId, 1);
}
/**
* 获取研报分类阅读排行榜
*/
public Set<Object> getReportCategoryReadCountRank() {
if (template.hasKey(CacheConstant.REPORT_CATEGORY_READ_COUNT)) {
//0,-1的参数代表查询该key下的所有value
return template.opsForZSet().reverseRange(CacheConstant.REPORT_CATEGORY_READ_COUNT, 0, -1);
} else {
return new HashSet<>();
}
}
/**
* 获取研报分类阅读排行榜具体分类次数
*/
public Double getReportCategoryBuyCount(Object o) {
return template.opsForZSet().score(CacheConstant.REPORT_CATEGORY_BUY_COUNT, o);
}
-
这里我们将分类在数据库中的主键id存进去,以便按顺序查出来之后去数据库中查询对应的分类名。
-
score值就是该文章的阅读次数,可以直接调用接口获取
4.Rank.java
public static class Rank implements Serializable {
private Long id;
private String name;
private Integer times;
}
5.StatisticService.java
@Autowired
private CacheService cacheService;
public StatisticsPlatformResponse platform() {
//查询一级分类下的研报阅读排行
List<Rank> readRank = new ArrayList<>();
//一次性查询出所有一级分类,然后和分类排行榜的对应id去匹配
List<ReportCategory> oneCategoryList = reportCategoryService.findAllOneCategory();
Set<Object> readCountRank = cacheService.getReportCategoryReadCountRank();
if (readCountRank != null) {
for (Object o : readCountRank) {
Rank singReadRank = getRank(oneCategoryList, (Number) o);
singReadRank.setTimes(cacheService.getReportCategoryReadCount(o).intValue());
readRank.add(singReadRank);
}
}
return readRank;
}
private Rank getRank(List<ReportCategory> oneCategoryList, Number o) {
Rank rank = new Rank();
rank.setId(o.longValue());
rank.setName(getCategoryName(o.longValue(), oneCategoryList));
return rank;
}
/**
* 获取分类名
*
* @param categoryId
* @param list
* @return
*/
private String getCategoryName(Long categoryId, List<ReportCategory> list) {
for (ReportCategory category : list) {
if (category.getId().equals(categoryId)) {
return category.getName();
}
}
return null;
}