SpringBoot中使用Redis实现排行榜功能,并考虑到 当用户积分相同时,要求按最后更新时间升序

Redis 实现排行榜主要依赖于其有序集合zset(Sorted Set)数据结构。

zset中可以存储不重复的元素集合,并为每个元素关联一个浮点数分数(score),Redis 会根据这个分数自动对集合中的元素进行排序。

可以使用 `ZADD` 命令来向有序集合中添加元素,将上面列表中:用户id作为元素、积分作为分数

获取用户积分排行
使用 `ZREVRANGE` 命令(从高到低排序)或 `ZRANGE` 命令(从低到高排序)来获取排行榜的前几名

# 积分相同时,按最后更新时间升序,解决思路

可以将zset中的score设置为一个浮点数,其中整数部分为积分,小数部分为最后更新时间时间戳,算法如下

## score = 积分 + 时间戳/10的13次方

> 这里为什么要除以10的13次方?由于时间戳的长度是13位,除以10的13次方,可以将其移到小数点的右边

对上面表格,处理之后,变成了下面这样

| 用户id | 积分 | 最后更新时间时间戳(毫秒) | score |
| ------ | ---- | ------------------------ | ----------------- |
| user1 | 100 | 1720663200002 | 100.1720663200002 |
| user2 | 100 | 1720663200001 | 100.1720663200001 |
| user3 | 150 | 1720663200000 | 150.1720663200000 |

按score降序排序后,是:user3 (150.1720663200000) > user1 (100.1720663200002) > user2( 100.1720663200001)

和预期的不一样,user2的最后更新时间是小于user1的,user2应该排在user1之前,怎么办呢

需要再做一次转换

## score = 积分 + (1 - 时间戳/10的13次方)

处理后,表格变成了下面这样

| 用户id | 积分 | 最后更新时间时间戳(毫秒) | score |
| ------ | ---- | ------------------------ | ----------------- |
| user1 | 100 | 1720663200002 | 100.8279336799998 |
| user2 | 100 | 1720663200001 | 100.8279336799999 |
| user3 | 150 | 1720663200000 | 150.8279336800000 |

按降序排序后,是:user3 (150.8279336800000) > user2 (100.8279336799999) > user1(100.8279336799998)

这样就达到了预期的目的

 





具体实现代码如下
1 pojo类

@Data
public class UserPointsReq {//用户idprivate String userId;//积分private Integer points;//最后更新时间(时间戳毫秒)private Long updateTime;
}/////返回结果封装
@Data
public class UserRanking {private String userId;private double redisScore;
}

2 核心代码

import com.example.demo_26.redis_paiming.dto.UserPointsReq;
import com.example.demo_26.redis_paiming.dto.UserRanking;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;/*** 用户积分排行榜控制器* <p>* 该控制器提供了用户积分的插入和查询用户积分排行榜的功能* 使用Redis的有序集合(ZSet)来存储用户积分,以实现高效查询和排序* </p>*/
@RestController
public class UserRankingController {public static final String redis_key="user:ranking";@Autowiredprivate StringRedisTemplate stringRedisTemplate;/*** 用户积分批量插入到Redis** @param userPointsReqList 包含用户积分和更新时间的列表* @return 总是返回true,表示操作成功*/@PostMapping("/addUserPoint")public boolean addUserPoint(@RequestBody List<UserPointsReq> userPointsReqList) {for (UserPointsReq userPointsReq : userPointsReqList) {String userId = userPointsReq.getUserId();//先按积分降序,积分相同时按照最后更新时间升序,score = 积分 + (1 - 时间戳/10的13次方)double score = userPointsReq.getPoints() + (1 - userPointsReq.getUpdateTime() / 1e13);this.stringRedisTemplate.opsForZSet().add(redis_key, userId, score);}return true;}/*** 获取用户积分排行榜(倒序)** @param topN 前多少名* @return 前topN的用户积分排名列表*/@GetMapping("/userRankings")public List<UserRanking> userRankings(@RequestParam("topN") int topN) {// 从Redis中获取倒序排列的用户积分数据Set<ZSetOperations.TypedTuple<String>> typedTuples = this.stringRedisTemplate.opsForZSet().reverseRangeWithScores(redis_key, 0, topN - 1);List<UserRanking> userRankingList = new ArrayList<>();for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {UserRanking userRanking = new UserRanking();userRanking.setUserId(typedTuple.getValue());userRanking.setRedisScore(typedTuple.getScore());userRankingList.add(userRanking);}return userRankingList;}}

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

文件丢失一键找回,四大数据恢复免费版工具推荐!

丢失数据的情况虽然不经常出现&#xff0c;但一旦出现都会让人头疼不已&#xff0c;而这时候&#xff0c;要如何恢复丢失的数据呢&#xff1f;一款免费好用的数据恢复工具就派上用场了&#xff01;接下来就为大家推荐几款好用的数据恢复工具&#xff01; 福昕数据恢复 直达链…

Redis list 类型

list类型 类型介绍 列表类型 list 相当于 数组或者顺序表 list内部的编码方式更接近于 双端队列 &#xff0c;支持头插 头删 尾插 尾删。 需要注意的是&#xff0c;Redis的下标支持负数下标。 比如数组大小为5&#xff0c;那么要访问下标为 -2 的值可以理解为访问 5 - 2 3 …

【韩顺平Java笔记】第8章:面向对象编程(中级部分)【272-284】

272. 包基本介绍 272.1 看一个应用场景 272.2 包的三大作用 272.3 包的基本语法 273. 包原理 274. 包快速入门 在不同的包下面创建不同的Dog类 275. 包命名 276. 常用的包 一个包下,包含很多的类,java 中常用的包有: java.lang.* //lang 包是基本包&#xff0c;默认引入&…

农业政策与市场分析:解读当前政策导向下的农业发展趋势

在快速变化的全球经济格局中&#xff0c;农业作为国家稳定发展的基石&#xff0c;其政策走向与市场动态备受瞩目。本文将深入剖析当前的农业政策背景&#xff0c;探讨其对设计的导向作用&#xff0c;以及市场趋势的反馈与影响&#xff0c;为农业可持续发展提供洞见。 1. 政策背…

【大模型理论篇】大模型相关的周边技术分享-关于《NN and DL》的笔记

本文所要介绍的一本书《Neural Networks and Deep Learning》&#xff0c;该书作者Michael Nielsen&#xff0c;Y Combinator Research的研究员&#xff0c;是多年之前自己看的一本基础书籍&#xff0c;很适合入门了解一些关于深度学习的概念知识&#xff0c;当然也包含了一些小…

MyBatis 批量插入方案

MyBatis 批量插入 MyBatis 插入数据的方法有几种&#xff1a; for 循环&#xff0c;每次都重新连接一次数据库&#xff0c;每次只插入一条数据。 在编写 sql 时用 for each 标签&#xff0c;建立一次数据库连接。 使用 MyBatis 的 batchInsert 方法。 下面是方法 1 和 2 的…

三相逆变器中LCL滤波器分析

1.LCL滤波器 传统三相逆变器使用的是L型滤波器&#xff0c;其设计简单&#xff0c;但也存在着一些问题&#xff0c;如在同样的滤波效果下&#xff0c;L型滤波器电感尺寸、重量较大&#xff0c;成本较高&#xff0c;并且随着电感值的增大&#xff0c;其上的电压降增加比较明显&…

【MySQL必知会】事务

目录 &#x1f308;前言&#x1f308; &#x1f4c1; 事务概念 &#x1f4c1; 事务操作 &#x1f4c1; 事务提交方式 &#x1f4c1; 隔离级别 &#x1f4c1; MVCC &#x1f4c2; 3个隐藏列字段 &#x1f4c2; undo日志 &#x1f4c2; Read View视图 &#x1f4c1; RR和R…

【GESP】C++一级练习BCQM3028,输入-计算-浮点型格式化输出

目前的几道题主要围绕浮点型的计算和格式化输出。强化基础语法练习。 详解详见&#xff1a;https://www.coderli.com/gesp-1-bcqm3028/ 【GESP】C一级练习BCQM3028&#xff0c;输入-计算-浮点型格式化输出 | OneCoder目前的几道题主要围绕浮点型的计算和格式化输出。强化基础语…

说说BPMN概念及应用

BPMN&#xff08;Business Process Modeling and Notation&#xff09;即业务流程建模与标注&#xff0c;是一种由OMG&#xff08;Object Management Group&#xff0c;对象管理组织&#xff09;制定的业务流程建模语言。以下是对BPMN标准的详细解释&#xff1a; 一、BPMN的起…

短剧系统源码短剧平台开发(H5+抖小+微小)部署介绍流程

有想法加入国内短剧赛道的请停下脚步&#xff0c;耐心看完此篇文章&#xff0c;相信一定会对您有所帮助的&#xff0c;下面将排序划分每一个步骤&#xff0c;短剧源码、申请资料、服务器选择、部署上架到正常运行等几个方面&#xff0c;整理了一些资料&#xff0c;来为大家举例…

Spring Boot助力医院数据管理

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常适…

MySQL进阶学习一(2024.10.07版)

2024-10-06 -------------------------------------------------------------------------------------------------------------------------------- 1.一条SQL语句是如何执行的 单进程的多线程模型 MySQL的物理目录 show global variables like "%basedir%"; …

LSTM时序预测 | Python实现LSTM长短期记忆神经网络时间序列预测

本文内容&#xff1a;Python实现LSTM长短期记忆神经网络时间序列预测&#xff0c;使用的数据集为AirPassengers 目录 数据集简介 1.步骤一 2.步骤二 3.步骤三 4.步骤四 数据集简介 AirPassengers 数据集的来源可以追溯到经典的统计和时间序列分析文献。原始数据集由 Box,…

面向对象特性中 继承详解

目录 概念&#xff1a; 定义&#xff1a; 定义格式 继承关系和访问限定符 基类和派生类对象赋值转换&#xff1a; 继承中的作用域&#xff1a; 派生类的默认成员函数 继承与友元&#xff1a; 继承与静态成员&#xff1a; 复杂的菱形继承及菱形虚拟继承&#xff1a; 虚…

VGG16模型实现MNIST图像分类

MNIST图像数据集 MNIST&#xff08;Modified National Institute of Standards and Technology&#xff09;是一个经典的机器学习数据集&#xff0c;常用于训练和测试图像处理和机器学习算法&#xff0c;特别是在数字识别领域。该数据集包含了大约 7 万张手写数字图片&#xf…

喜讯 | 攸信技术入选第六批专精特新“小巨人”企业

日前&#xff0c;根据工信部评审结果&#xff0c;厦门市工业和信息化局公示了第六批专精特新“小巨人”企业和第三批专精特新“小巨人”复核通过企业名单&#xff0c;其中&#xff0c;厦门攸信信息技术有限公司进入第六批专精特新“小巨人”企业培育。 “专精特新”企业是指具有…

图像分割恢复方法

传统的图像分割方法主要依赖于图像的灰度值、纹理、颜色等特征&#xff0c;通过不同的算法将图像分割成多个区域。这些方法通常可以分为以下几类&#xff1a; 1.基于阈值的方法 2.基于边缘的方法 3.基于区域的方法 4.基于聚类的方法 下面详细介绍这些方法及其示例代码。 1. 基…

代码随想录--栈与队列--用栈实现队列

队列是先进先出&#xff0c;栈是先进后出。 如图所示&#xff1a; 题目 使用栈实现队列的下列操作&#xff1a; push(x) – 将一个元素放入队列的尾部。 pop() – 从队列首部移除元素。 peek() – 返回队列首部的元素。 empty() – 返回队列是否为空。 示例: MyQueue qu…

draw.io 设置默认字体及添加常用字体

需求描述 draw.io 是一个比较好的开源免费画图软件。但是其添加容器或者文本框时默认的字体是 Helvetica&#xff0c;一般的期刊、会议论文或者学位论文要求的英文字体是 Times New Roman&#xff0c;中文字体是 宋体&#xff0c;所以一般需要在文本字体选项里的下拉列表选择 …