SpringBoot+Redission实现排行榜功能

SpringBoot+Redission实现排行榜功能

demo地址:ranking-demo: 排行榜DEMO (gitee.com)

一、业务需求

实现一个排行榜,要求按照分数和达成这个分数的时间排序,即相同分数下,时间早的在上面

二、Redis中的zSet(有序集合)

1.简介

Redis 的 zSet(也称为有序集合)是一种特殊的数据结构,它同时包含了集合和有序列表的特性。在 zSet 中,每个成员都有一个分数(score)与之关联,这个分数可以是浮点数,用于对集合中的元素进行排序。

2.特点

  1. 元素唯一:就像集合一样,zSet 中不允许有重复成员。
  2. 有序性:集合中的元素按照其关联的分数值进行升序排序。
  3. 操作丰富:支持添加、删除成员,获取指定范围的成员,根据分数查询成员,计算交集、并集、差集等操作。

3.常用命令

  • ZADD:向有序集合中添加一个或多个成员,或者更新已存在成员的分数。
  • ZRANGE:返回有序集合中指定区间内的成员,通过索引位置来获取,从0开始。
  • ZRANGEBYSCORE:返回有序集合中指定分数区间的成员。
  • ZCARD:获取有序集合的成员数量。
  • ZREM:移除有序集合中的一个或多个成员。
  • ZREVRANGE:类似于 ZRANGE,但返回的是从高分到低分的成员。
  • ZINCRBY:为有序集合中的成员的分数加上给定值。
  • ZCOUNT:计算有序集合中指定分数区间的成员数量。
  • ZRANK/ZREVRANK:获取成员在有序集合中的排名,ZRANK 是从低分到高分,ZREVRANK 是从高分到低分。

4.测试

> ZADD zsetkey 1 member1
1
> ZADD zsetkey 1 member2
1
> ZADD zsetkey 1 member9
1
> ZADD zsetkey 1 member5
1
> ZREVRANGE zsetkey 0 10 WITHSCORES
member9
1
member5
1
member2
1
member1
1

5.总结

zSet可以很好的实现分数排序,但是在相同的分数下,会按照成员的名称进行排序,所以要在此基础上增加时间

三、增加时间数据

为了增加完成时间,我们可以引进一个倒计时的概念,假设一共9秒

用户A在获得1分的时候在第2秒,那可以在Redis中存储1*10+(9-2) => 18

用户B在获得1分的时候在第6秒,那可以在Redis中存储1*10+(9-6) => 13

这样我们在获取分数的时候,可以倒推出分数和完成时间

四、SpringBoot代码

1.引入Redis和Redission依赖

<!-- redis -->  
<dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-data-redis</artifactId>  
</dependency>  <!-- redisson -->  
<dependency>  <groupId>org.redisson</groupId>  <artifactId>redisson-spring-boot-starter</artifactId>  <version>3.20.1</version>  
</dependency>

2.application.yml配置

--- # redis配置  
spring:  redis:  # 地址  host: localhost  # 端口,默认为6379  port: 6379  # 数据库索引  database: 0  # 密码(如没有密码请注释掉)  # password:    # 连接超时时间  timeout: 10s  # 是否开启ssl  ssl: false  --- # redisson配置  
redisson:  # redis key前缀  keyPrefix: ${spring.application.name}  # 线程池数量  threads: 4  # Netty线程池数量  nettyThreads: 8  # 单节点配置  singleServerConfig:  # 客户端名称  clientName: ${spring.application.name}  # 最小空闲连接数  connectionMinimumIdleSize: 8  # 连接池大小  connectionPoolSize: 32  # 连接空闲超时,单位:毫秒  idleConnectionTimeout: 10000  # 命令等待超时,单位:毫秒  timeout: 3000  # 发布和订阅连接池大小  subscriptionConnectionPoolSize: 50

3.Java代码

Constant
/**  * @author Baisu  * @classname RankingConstant  * @description 排行榜常量数据  * @since 2024/5/6  */
public class RankingConstant {  public static final Long BASIC_QUANTITY = 10000000000000L;  public static final Long MAXIMUM_TIME_TIMIT = 29991231235959L;  
}
Controller
import cn.hutool.core.date.DateTime;  
import cn.hutool.core.date.DateUtil;  
import com.ranking.demo.common.R;  
import com.ranking.demo.common.constant.RankingConstant;  
import com.ranking.demo.demain.RankingVo;  
import com.ranking.demo.utils.RankingUtil;  
import com.ranking.demo.utils.RedisKey;  
import com.ranking.demo.utils.RedisUtil;  
import org.redisson.client.protocol.ScoredEntry;  
import org.springframework.beans.factory.annotation.Value;  
import org.springframework.web.bind.annotation.*;  import java.util.ArrayList;  
import java.util.Collection;  
import java.util.List;  /**  * @author Baisu  * @since 2024/4/28  */
@RestController  
public class DemoRankingController {  @Value("${spring.application.name}")  private String applicationName;  /**  * 项目启动测试方法  *  * @return applicationName  */    @GetMapping("")  public String demo() {  return applicationName;  }  /**  * 生成测试数据  *  * @return ok  */    @GetMapping("/generate_test_data")  public R<Object> generateTestData() {  RedisUtil.addScoreByMember(RedisKey.getRankingDemoKey(), 1L, "10001");  RedisUtil.addScoreByMember(RedisKey.getRankingDemoKey(), 2L, "10002");  RedisUtil.addScoreByMember(RedisKey.getRankingDemoKey(), 3L, "10003");  RedisUtil.addScoreByMember(RedisKey.getRankingDemoKey(), 4L, "10004");  RedisUtil.addScoreByMember(RedisKey.getRankingDemoKey(), 5L, "10005");  return R.ok();  }  /**  * 获取排行榜数据  *  * @param top 数量  * @return 排行榜数据  */  @GetMapping("/get_ranking")  public R<Object> getRanking(@RequestParam("top") Integer top) {  Collection<ScoredEntry<Object>> ranking = RedisUtil.getRanking(RedisKey.getRankingDemoKey(), 0, top - 1);  if (ranking.size() == 0) {  return R.fail("暂无排行榜数据");  }  List<RankingVo> list = new ArrayList<>();  for (ScoredEntry<Object> entry : ranking) {  RankingVo vo = new RankingVo();  vo.setMember(entry.getValue().toString());  vo.setScore(RankingUtil.getScore(entry.getScore()));  vo.setTime(RankingUtil.getTimeStr(entry.getScore()));  list.add(vo);  }  return R.ok(list);  }  /**  * 增加成员分数值  *  * @param member 成员  * @return 是否增加成功  */  @GetMapping("/add_score_by_member")  public R<Object> addScoreByMember(@RequestParam("member") String member) {  Double scoreByMember = RedisUtil.getScoreByMember(RedisKey.getRankingDemoKey(), member);  if (scoreByMember == null) {  scoreByMember = 0.0;  }  RedisUtil.addScoreByMember(RedisKey.getRankingDemoKey(), RankingUtil.getScore(scoreByMember) + 1, member);  return R.ok();  }  /**  * 获取成员分数值  *  * @param member 成员  * @return 分数值  */  @GetMapping("/get_score_by_member")  public R<Object> getScoreByMember(@RequestParam("member") String member) {  Double scoreByMember = RedisUtil.getScoreByMember(RedisKey.getRankingDemoKey(), member);  if (scoreByMember == null) {  return R.fail("该成员不存在");  }  RankingVo vo = new RankingVo();  vo.setMember(member);  vo.setScore(RankingUtil.getScore(scoreByMember));  vo.setTime(RankingUtil.getTimeStr(scoreByMember));  return R.ok(vo);  }  }
Domain
import lombok.Data;  /**  * @author Baisu  * @classname RankingVo  * @description 排行榜展示类  * @since 2024/5/6  */
@Data  
public class RankingVo {  /**  * 成员  */  private String member;  /**  * 分数值  */  private Long score;  /**  * 时间  */  private String time;  }
Utils
/**  * @author Baisu  * @classname RedisKey  * @description Redis索引  * @since 2024/5/6  */
public class RedisKey {  private static final String RANKING_DEMO_KEY = "ranking_demo";  public static String getRankingDemoKey() {  return RANKING_DEMO_KEY;  }  
}
import cn.hutool.core.date.DateTime;  
import cn.hutool.core.date.DateUtil;  
import cn.hutool.extra.spring.SpringUtil;  
import com.ranking.demo.common.constant.RankingConstant;  
import org.redisson.api.RScoredSortedSet;  
import org.redisson.api.RedissonClient;  
import org.redisson.client.protocol.ScoredEntry;  import java.util.Collection;  /**  * @author Baisu  * @classname RedisUtil  * @description Redis工具类  * @since 2024/5/6  */
public class RedisUtil {  private static final RedissonClient REDISSON_CLIENT = SpringUtil.getBean(RedissonClient.class);  /**  * 向有序集合中添加指定分数的成员  *  * @param key    有序集索引  * @param score  分数  * @param member 成员  * @return 是否成功  */  public static boolean addScoreByMember(String key, Long score, String member) {  RScoredSortedSet<String> rScoredSortedSet = REDISSON_CLIENT.getScoredSortedSet(key);  double v = score * RankingConstant.BASIC_QUANTITY + (RankingConstant.MAXIMUM_TIME_TIMIT - Long.parseLong(DateUtil.format(DateTime.now(), RankingUtil.FORMAT)));  return rScoredSortedSet.add(v, member);  }  /**  * 返回有序集中成员的分数值  *  * @param key    有序集索引  * @param member 成员  * @return 分数值(Double)  */    public static Double getScoreByMember(String key, String member) {  RScoredSortedSet<Object> scoredSortedSet = REDISSON_CLIENT.getScoredSortedSet(key);  return scoredSortedSet.getScore(member);  }  /**  * 返回有序集中指定位置的成员集合  *  * @param key   有序集索引  * @param start 开始索引  * @param end   结束索引  * @return 成员集合  */  public static Collection<ScoredEntry<Object>> getRanking(String key, int start, int end) {  RScoredSortedSet<Object> rScoredSortedSet = REDISSON_CLIENT.getScoredSortedSet(key);  return rScoredSortedSet.entryRangeReversed(start, end);  }  }
import cn.hutool.core.date.DateUtil;  
import com.ranking.demo.common.constant.RankingConstant;  /**  * @author Baisu  * @classname RankingUtil  * @description 排行榜工具类  * @since 2024/5/7  */public class RankingUtil {  public static final String FORMAT = "yyyyMMddHHmmss";  public static Long getScore(Double score) {  return Math.round(Math.floor(score / RankingConstant.BASIC_QUANTITY));  }  public static String getTimeStr(Double score) {  return String.valueOf(DateUtil.parse(String.valueOf(RankingConstant.MAXIMUM_TIME_TIMIT - Math.round(Math.floor(score)) % RankingConstant.BASIC_QUANTITY)));  }  
}

五、接口文档

ranking_demo接口文档

六、参考文章

Redis常用命令对应到Redisson对象操作_redisson rscript中eval-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/7405.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Flutter 中的 @immutable:深入解析与最佳实践

在 Flutter 开发中&#xff0c;immutable 注释扮演着至关重要的角色&#xff0c;用于标记不可变类。不可变类顾名思义&#xff0c;其状态一旦创建便不可更改&#xff0c;这与可变类截然不同。后者允许在创建后对实例进行修改。 immutable 的利好 引入不可变类可以带来诸多优势…

GO日志打印添加goroutineid

今天想给日志添加一个前缀&#xff0c;以区分不同goroutine的日志&#xff0c;方便做并发问题的排查&#xff0c;做日志跟踪。 为了解决goroutineid&#xff0c;网上各出奇招&#xff0c;有的使用runtime包未公开的方法获取&#xff1a; func Goid() int {defer func() {if e…

如何在Windows 11中查找产品密钥?这里提供两种办法

Windows 11使用产品密钥来确保你的操作系统是正版的,换句话说,不是盗版的。你可以在你的电脑上找到正在使用的产品密钥,如果你自己购买了Windows并需要重新安装,该密钥特别有用。 什么是Windows产品密钥 产品密钥是微软确保所使用的Windows副本是正版的方法。当激活Windo…

docker容器 怎么查看运行日志

在Docker中&#xff0c;查看容器的运行日志可以使用docker logs命令。该命令允许你获取容器的日志输出&#xff0c;这对于调试和监控容器的状态非常有用。 以下是docker logs命令的一些常用用法&#xff1a; 基本用法 docker logs [OPTIONS] CONTAINERCONTAINER 是容器的ID或…

【Pytorch】3.Transforms的运用

什么是Transforms 在PyTorch中&#xff0c;transforms是用于对数据进行预处理、增强和变换的操作集合。transforms通常用于数据载入和训练过程中&#xff0c;可以包括数据的归一化、裁剪、翻转、旋转、缩放等操作&#xff0c;以及将数据转换成PyTorch可以处理的Tensor格式。 Tr…

leetcode---岛屿数量

. - 力扣&#xff08;LeetCode&#xff09; 代码&#xff1a; //岛屿题目的思想&#xff1a;二维矩阵图的DFS就是&#xff0c;上下左右遍历如果是0或者出界的话就return //规定的是陆地上下左右是水的话它就是岛屿。当遍历矩阵图中每一个点&#xff0c; //在调用递归算法之前…

高频次的低价监测如何实现

品牌在做控价的过程中&#xff0c;需要对渠道中的低价数据进行监测&#xff0c;但价格数据变化快&#xff0c;涉及的促销信息也很多&#xff0c;如何将这些变化的数据监测到位&#xff0c;同时对于低价的凭证还要截图留证&#xff0c;以便有效的进行渠道治理&#xff0c;这就需…

【redis】redis持久化分析

目录 持久化Redis持久化redis持久化的方式持久化策略的设置1. RDB&#xff08;快照&#xff09;fork(多进程)RDB配置触发RDB备份自动备份手动执行命令备份&#xff08;save | bgsave&#xff09;flushall命令主从同步触发动态停止RDB RDB 文件恢复验证 RDB 文件是否被加载 RDB …

【海豚调度 开机启动】dophischeduler 如何开启开机自启动功能

DolphinScheduler 是一个分布式、去中心化的大数据工作流调度系统&#xff0c;支持大数据任务调度。若要设置 DolphinScheduler 开机自启动&#xff0c;通常需要将其配置为系统服务。以下是一般步骤&#xff0c;具体操作可能因操作系统的不同而有所差异&#xff1a; 在 Linux …

AI大模型探索之路-训练篇16:大语言模型预训练-微调技术之LoRA

系列篇章&#x1f4a5; AI大模型探索之路-训练篇1&#xff1a;大语言模型微调基础认知 AI大模型探索之路-训练篇2&#xff1a;大语言模型预训练基础认知 AI大模型探索之路-训练篇3&#xff1a;大语言模型全景解读 AI大模型探索之路-训练篇4&#xff1a;大语言模型训练数据集概…

图像处理(二)

图像处理&#xff08;2&#xff09; 裁剪图片 from skimage import io,dataiimg io.imread(rD:\工坊\图像处理\十个勤天2.png)roiiimg[50:150,120:200,:]io.imshow(roi) 运行结果&#xff1a; 将图片进行二值化 from skimage import io,data,colorimg io.imread(r"…

影响项目成功的六个“致命”错误

项目经理作为项目的负责人&#xff0c;肩负着巨大的责任和挑战。他们需要具备专业知识、出色的综合管理能力以及敏锐的洞察力&#xff0c;以便在项目执行过程中及时关注项目动态&#xff0c;处理好各种问题&#xff0c;并避免那些可能影响项目实施的致命错误。 一、缺乏明确的…

基于Netty的websocket的简单介绍

1、websocket简介 「WebSocket」是一种在单个TCP连接上进行全双工通信的协议。 「WebSocket」使得客户端和服务器之间的数据交换变得更加简单&#xff0c;允许服务端主动向客户端推送数据。在「WebSocket API」中&#xff0c;浏览器和服务器只需要完成一次握手&#xff0c;两…

羊大师解析,鲜为人知的羊奶冷知识

羊大师解析&#xff0c;鲜为人知的羊奶冷知识 羊奶的脂肪球更小&#xff1a;相较于牛奶&#xff0c;羊奶中的脂肪球直径更小&#xff0c;这有助于其更快地被人体消化和吸收。 羊奶含有更多的中链脂肪酸&#xff1a;羊奶中含有较多的中链脂肪酸&#xff08;MCT&#xff09;&am…

5个好用AI绘画工具,让你秒变艺术家!

AI绘画现在可谓是相当火爆&#xff0c;各种AI绘画工具如雨后春笋般涌出。很多人想自己尝试用AI来创作&#xff0c;却不知道使用什么工具&#xff0c;今天就给大家分享5个好用AI绘画工具&#xff0c;有的只需一段文字便可生成一幅美轮美奂的大作&#xff0c;让你秒变艺术家&…

Spring Cloud Stream的作用和用法

Spring Cloud Stream是一个用于构建消息驱动型微服务的框架&#xff0c;它在Spring Cloud生态系统中扮演着关键角色。以下是关于Spring Cloud Stream的作用和用法的详细描述&#xff1a; 一、作用 简化消息中间件集成&#xff1a;Spring Cloud Stream旨在简化和统一消息中间件…

基于springboot实现的疫情网课管理系统

开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven…

Implicit Diffusion Models for Continuous Super-Resolution

CVPR2023https://github.com/Ree1s/IDM问题引入&#xff1a; – LIIF方法可以实现任意分辨率的输出&#xff0c;但是因为是regression-based方法&#xff0c;所以得到的结果缺少细节&#xff0c;而生成的方法(gan-based,flow-based&#xff0c;diffusion-based等)可以生成细节&…

虚幻引擎中的投影技术主要用于创建多屏交互式内容和沉浸式显示环境

首先&#xff0c;在虚幻引擎&#xff08;UE4&#xff09;中&#xff0c;nDisplay插件是一个强大的工具&#xff0c;它允许开发者创建多屏投影系统。这种系统可以是由多个相邻的物理屏幕组成&#xff0c;例如Powerwall显示器&#xff0c;或者使用多个投影仪将3D环境投射到物理表…

oxford-iiit-pet 数据集

文章目录 项目说明数据集说明 oxford-iiit-pet 代码实现数据处理整理原始数据-分类划分 训练集和测试集 项目说明 数据集说明 oxford-iiit-pet 数据官方网站&#xff1a; https://www.robots.ox.ac.uk/~vgg/data/pets/ 下载 .torrent 文件&#xff0c;然后下载完整文件。 主要…