Redis ZSet数据结构实现排行榜功能

一. 使用场景

公司新项目要求, 实现每日排行榜以及各省排行榜

二. 功能实现(Java)

1. 排行榜数据插入及更新

 /*** @date: 2022/11/2* @description: 每天凌晨6点生成排行榜数据*/@Override@Scheduled(cron = "0 0 6 * * ?")public void generate() {try {// 今日排行榜String dailyKey = today + "_PERSON_RANK";String today = LocalDate.now().format(DateTimeFormatter.ISO_DATE);// 随机获取表里的机器人信息Result<ArrayList<UserPlainVO>> userIdResults = userManager.getRankUserIds();ArrayList<UserPlainVO> userPlainVOS = userIdResults.getResults();if (Objects.equals("200",userIdResults.getCode()) && CollectionUtils.isNotEmpty(userPlainVOS)) {for (UserPlainVO userPlainVO : userPlainVOS) {// 作答时间为250到750的随机数int usedTime = getRandomNum(250,750);redisTemplate.opsForZSet().add(dailyKey,userPlainVO.getUserId(),usedTime);}}// 设置次日凌晨6点key失效String nextDayMorning = LocalDate.now().plusDays(1).format(DateTimeFormatter.ISO_DATE) + " 06:00:00";Date nextDay = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(nextDayMorning);redisTemplate.expireAt(dailyKey,nextDay);// 获取全部省份名称,初始值为5K-2W的随机数,每日累加3K-5K的随机数String provinceKey = "PROVINCE_RANK";List<String> provinces = sysBasService.getRankProvinces(); // 从字典表获取for (String province : provinces) {// 判断该成员省是否已存入缓存中Double score = redisTemplate.opsForZSet().score(provinceKey, province);if (Objects.isNull(score)) {// 不在赋予初始值(1W-2W)int scoreFirst = getRandomNum(10000,20000);redisTemplate.opsForZSet().add(provinceKey,province,scoreFirst);} else {// 存在则累加int scoreAdd = getRandomNum(3000,5000);redisTemplate.opsForZSet().incrementScore(provinceKey,province,scoreAdd);}}} catch (ParseException e) {throw new RuntimeException(e);}}/*** @date: 2022/11/8* @description: 获取范围内的随机整数* @param min:* @param max:*/public static int getRandomNum(int min, int max) {return (int) (Math.random() * (max - min + 1) + min);}

2. 排行榜数据获取

2.1 创建排行榜数据接收实体

import io.swagger.annotations.ApiModelProperty;import java.io.Serializable;
import java.util.List;/*** @author xiazz* @Date 2022/10/14* @Description:*/
public class RankInfoVO implements Serializable {@ApiModelProperty("用户排名: -1表示未上榜")private Long userRank;@ApiModelProperty("用户通关总耗时: -1表示未通关")private Integer userSumTime;@ApiModelProperty("用户所在地区排名: -1表示未上榜")private Long userAreaRank;@ApiModelProperty("今日排行榜列表")private List<DailyRankVO> dailyRankVOS;@ApiModelProperty("省份排行榜列表")private List<ProvinceRankVO> provinceRankVOS;@ApiModelProperty("用户名")private String currentUsername;@ApiModelProperty("用户头像")private String currentImgUrl;public Long getUserRank() {return userRank;}public void setUserRank(Long userRank) {this.userRank = userRank;}public Integer getUserSumTime() {return userSumTime;}public void setUserSumTime(Integer userSumTime) {this.userSumTime = userSumTime;}public Long getUserAreaRank() {return userAreaRank;}public void setUserAreaRank(Long userAreaRank) {this.userAreaRank = userAreaRank;}public List<DailyRankVO> getDailyRankVOS() {return dailyRankVOS;}public void setDailyRankVOS(List<DailyRankVO> dailyRankVOS) {this.dailyRankVOS = dailyRankVOS;}public List<ProvinceRankVO> getProvinceRankVOS() {return provinceRankVOS;}public void setProvinceRankVOS(List<ProvinceRankVO> provinceRankVOS) {this.provinceRankVOS = provinceRankVOS;}public String getCurrentUsername() {return currentUsername;}public void setCurrentUsername(String currentUsername) {this.currentUsername = currentUsername;}public String getCurrentImgUrl() {return currentImgUrl;}public void setCurrentImgUrl(String currentImgUrl) {this.currentImgUrl = currentImgUrl;}
}

日榜数据

import io.swagger.annotations.ApiModelProperty;import java.io.Serializable;/*** @author xiazz* @Date 2022/10/18* @Description:*/
public class DailyRankVO implements Serializable {@ApiModelProperty("用户名")private String username;@ApiModelProperty("通关耗时: -1表示未通关")private Integer sumUsedTime;@ApiModelProperty("排名: -1表示未上榜")private Long rank;@ApiModelProperty("用户头像")private String imgUrl;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public Integer getSumUsedTime() {return sumUsedTime;}public void setSumUsedTime(Integer sumUsedTime) {this.sumUsedTime = sumUsedTime;}public Long getRank() {return rank;}public void setRank(Long rank) {this.rank = rank;}public String getImgUrl() {return imgUrl;}public void setImgUrl(String imgUrl) {this.imgUrl = imgUrl;}
}

省榜

import io.swagger.annotations.ApiModelProperty;import java.io.Serializable;/*** @author xiazz* @Date 2022/10/18* @Description:*/
public class ProvinceRankVO implements Serializable {@ApiModelProperty("省份")private String province;@ApiModelProperty("排名")private Long rank;@ApiModelProperty("通关人数")private Integer passCount;public String getProvince() {return province;}public void setProvince(String province) {this.province = province;}public Long getRank() {return rank;}public void setRank(Long rank) {this.rank = rank;}public Integer getPassCount() {return passCount;}public void setPassCount(Integer passCount) {this.passCount = passCount;}
}

2.2 获取排行榜数据(日榜前50, 省榜全部)

/*** @param openid: 小程序用户的唯一凭证* @param rankType: 1日榜 2省榜* @date: 2022/10/18* @description: 排行榜*/@Overridepublic net.huashitong.ssydt.provider.web.model.Result<RankInfoVO> getRank(String openid, Integer rankType) {long t1 = System.currentTimeMillis();RankInfoVO rankInfoVO = new RankInfoVO();// 用户名String userId = "";String userProvince = "";// 调用用户服务的接口获取用户信息Result<User> userResult = userManager.info(openid);User user = userResult.getResults();if (Objects.equals("200",userResult.getCode()) && Objects.nonNull(user)) {userId = user.getUserId();userProvince = user.getProvince();rankInfoVO.setCurrentUsername(StringUtils.isNotBlank(user.getNickname()) ? user.getNickname() : "");// 用户名rankInfoVO.setCurrentImgUrl(StringUtils.isNotBlank(user.getImageUrl()) ? user.getImageUrl() : ""); // 用户头像}String today = LocalDate.now().format(DateTimeFormatter.ISO_DATE);if (rankType == 1) {String dailyKey = today + "_PERSON_RANK";// 用户排名if (StringUtils.isBlank(userId)) {rankInfoVO.setUserRank((long) -1);rankInfoVO.setUserSumTime(-1);} else {Long userRank = redisTemplate.opsForZSet().rank(dailyKey, userId);rankInfoVO.setUserRank(Objects.isNull(userRank) ? -1 : userRank + 1);// 用户通关总耗时Double score = redisTemplate.opsForZSet().score(dailyKey, userId);rankInfoVO.setUserSumTime(Objects.isNull(score) ? -1 : score.intValue());}// 获取今日排行榜ArrayList<DailyRankVO> dailyRankVOS = new ArrayList<>();Set<ZSetOperations.TypedTuple<Object>> set = redisTemplate.opsForZSet().rangeWithScores(dailyKey, 0, 49);for (ZSetOperations.TypedTuple<Object> tuple : set) {DailyRankVO dailyRankVO = new DailyRankVO();String dailyUserId = (String) tuple.getValue(); // 用户idResult<User> dailyUserResult = userManager.get(dailyUserId);User dailyUser = dailyUserResult.getResults();if (!Objects.equals("200",dailyUserResult.getCode()) || Objects.isNull(dailyUser)) {throw new ServiceException("100210","用户不存在");}dailyRankVO.setUsername(StringUtils.isNotBlank(dailyUser.getNickname()) ? dailyUser.getNickname() : ""); // 用户名dailyRankVO.setImgUrl(StringUtils.isNotBlank(dailyUser.getImageUrl()) ? dailyUser.getImageUrl() : ""); // 头像Double dailyUserSumTime = tuple.getScore(); // 通关耗时dailyRankVO.setSumUsedTime(Objects.isNull(dailyUserSumTime) ? -1 : dailyUserSumTime.intValue());// 排名Long rank = redisTemplate.opsForZSet().rank(dailyKey, dailyUserId);dailyRankVO.setRank(Objects.isNull(rank) ? -1 : rank + 1);dailyRankVOS.add(dailyRankVO);}rankInfoVO.setDailyRankVOS(dailyRankVOS);} else {// 省份String provinceKey = "PROVINCE_RANK";// 用户所在省份排名if (StringUtils.isBlank(userProvince)) {rankInfoVO.setUserAreaRank((long) -1);} else {Long userAreaRank = redisTemplate.opsForZSet().reverseRank(provinceKey, userProvince);rankInfoVO.setUserAreaRank(Objects.isNull(userAreaRank) || StringUtils.isBlank(userProvince) ? -1 : userAreaRank + 1);}Set<ZSetOperations.TypedTuple<Object>> set = redisTemplate.opsForZSet().reverseRangeWithScores(provinceKey, 0, -1);if (CollectionUtils.isNotEmpty(set)) {ArrayList<ProvinceRankVO> provinceRankVOS = new ArrayList<>();for (ZSetOperations.TypedTuple<Object> tuple : set) {ProvinceRankVO provinceRankVO = new ProvinceRankVO();String province = (String) tuple.getValue();// 省份provinceRankVO.setProvince(province);Double passCount = tuple.getScore(); // 通关人数provinceRankVO.setPassCount(Objects.isNull(passCount) ? -1 : passCount.intValue());// 排名Long rank = redisTemplate.opsForZSet().reverseRank(provinceKey, province);provinceRankVO.setRank(Objects.isNull(rank) ? -1 : rank + 1);provinceRankVOS.add(provinceRankVO);}rankInfoVO.setProvinceRankVOS(provinceRankVOS);}}long t2 = System.currentTimeMillis();logger.info("请求排行榜耗时: " + (t2-t1));return ResultUtils.getSuccessResults(rankInfoVO);}

Redis ZSet数据结构实现排行榜功能相关推荐

  1. php redis 搜索,PHP+Redis有序集合(zset)实现博客园阅读排行榜功能

    许多网站都有排行榜的功能,比如球员人气榜单.阅读排行榜,对于一些小网站,通过查数据库就能实现排行榜的功能,但是对于稍微有点用户量而且还是实时排名的网站,使用一些关系型数据库如(MySQL.Oracle ...

  2. Redis核心数据结构ZSET、GeoHash 、 Stream--排行榜、消息Pull推送、附近搜索、布隆过滤器 、IM聊天室

    ZSET.Geo . Stream redis zset数据结构 常用命令 排行榜 步骤一.初始化1个月的历史数据 步骤二:定时刷新数据 步骤3:排行榜查询接口 GeoHash 命令 附近酒店搜索实现 ...

  3. Java实现排行榜功能

    前言 最近项目需要开发一个排行榜功能,根据订单金额进行排名,同金额排名相同,不同则跳过,序列递增. 技术实现 MySQL 通过SQL语句也能实现,不过SQL过于复杂,也不好维护. SELECT CAS ...

  4. 【Redis】redis基本数据结构之ZSet

    介绍: ZSet数据结构类似于Set结构,只是ZSet结构中,每个元素都会有一个分值,然后所有元素按照分值的大小进行排列,相当于是一个进行了排序的链表. 如果ZSet是一个链表,而且内部元素是有序的, ...

  5. redis zset转set 反序列化失败_关于Redis中的五种数据结构,要知其然知其所以然...

    Redis作为Nosql的代表,想必大家已经再熟悉不过了,除了作为缓存来使用,Redis还提供了其他很多有用的功能,例如可作为消息队列.分布式锁.不隆过滤器.限流等功能使用.今天先来说一说redis作 ...

  6. 用redis做游戏内的各种排行榜功能

    一.前言 年前公司有很多活动要进行定制开发,活动中有游戏可以玩,最后对每个人的游戏分数进行排行展示,最终根据排名发放奖品.乍一看需求确实很简单,直接order by score一下不就完事了?需求确实 ...

  7. Redis实现世界杯排行榜功能(实战)

    点击上方"后端技术精选",选择"置顶公众号" 技术文章第一时间送达! 作者:俊俊的小熊饼干 cnblogs.com/wenjunwei/p/9754346.ht ...

  8. 【Redis最佳实践】使用DCS Redis实现排行榜功能

    本节基于华为云DCS Redis实践所编写,用于指导您在以下场景使用DCS Redis实现排行榜功能. 目录 场景介绍 代码示例 实践指导 运行结果 华为云DCS Redis介绍 场景介绍 在网页和A ...

  9. java基于Redis实现排行榜功能-附源码

    java基于Redis Zset实现排行榜功能 前言 做之前要思考的问题? Zset怎么存储需要的多个字段? 话不多说先上效果图 数据存储格式 代码 源码下载 闲暇之余,整理了一下之前利用Redis ...

最新文章

  1. 对于有A[0…N]有序的数组,判断是否存在A[i]=I 如果存在的话返回Index,
  2. 从0到1:构建强大且易用的规则引擎
  3. Android实现文件下载并自动安装apk包
  4. Logback配置输出sql
  5. java properties 属性,java 读取 properties 属性资料
  6. Java之ThreadLocal
  7. [转]jQuery设计思想
  8. 带你自学Python系列(十五):Python中类的用法(一)
  9. vivo X27发布日期官宣: 3月19日 三亚见!
  10. 机器学习中二分类逻辑回归的学习笔记
  11. 自动驾驶算法-滤波器系列(六)——10+种经典滤波算法
  12. 开发工具总结(6)之Android Studio模板配置详解(提高开发效率必备技能)
  13. 易经入门V2.0(体系最完整,推荐书目最完备,易经周易入门必收藏)
  14. C# 插入、删除Excel分页符
  15. 如何提高软件测试效率
  16. C/C++ 宏参数名前加#变为字符串
  17. Vcenter证书过期处理----Vcenter无法登录,“根证书错误”,“签名证书无效”,“503 service not available”
  18. 模糊数学Fuzzy Set第2讲——Fuzzy Logic Fuzzy Reasoning
  19. mysql数据库慕课答案_智慧树MySQL数据库设计与应用慕课答案
  20. openjudge 1.9.14 铺地毯

热门文章

  1. Linux文件系统和缓冲区
  2. rust读条闪退_rust读条慢卡loding | 手游网游页游攻略大全
  3. 2023和鲸夏令营创作活动【黄金价格分析预测】
  4. miui6和lbe 问题
  5. 【BZOJ2560】串珠子
  6. 如何创建基于webpack的vue项目和基于vite的vue项目及将现有vue项目变更为基于vite项目
  7. 2018年中国汽车产销量连续10年蝉联全球第一 增幅回落
  8. python以只读方式打开文本文件的参数_python:文件操作
  9. 软件工程网络15个人作业5(201521123010徐璐琳)——软件工程总结
  10. YOLOv5NEU-DET钢材表面缺陷任务