Redis实战 | 使用Redis 的有序集合(Sorted Set)实现排行榜功能,和Spring Boot集成

专栏集锦,大佬们可以收藏以备不时之需

Spring Cloud实战专栏:https://blog.csdn.net/superdangbo/category_9270827.html

Python 实战专栏:https://blog.csdn.net/superdangbo/category_9271194.html

Logback 详解专栏:https://blog.csdn.net/superdangbo/category_9271502.html

tensorflow专栏:https://blog.csdn.net/superdangbo/category_8691332.html

Redis专栏:https://blog.csdn.net/superdangbo/category_9950790.html

Spring Cloud实战:

Spring Cloud 实战 | 解密Feign底层原理,包含实战源码

Spring Cloud 实战 | 解密负载均衡Ribbon底层原理,包含实战源码

1024程序员节特辑文章:

1024程序员狂欢节特辑 | ELK+ 协同过滤算法构建个性化推荐引擎,智能实现“千人千面”

1024程序员节特辑 | 解密Spring Cloud Hystrix熔断提高系统的可用性和容错能力

1024程序员节特辑 | ELK+ 用户画像构建个性化推荐引擎,智能实现“千人千面”

1024程序员节特辑 | OKR VS KPI谁更合适?

1024程序员节特辑 | Spring Boot实战 之 MongoDB分片或复制集操作

Spring实战系列文章:

Spring实战 | Spring AOP核心秘笈之葵花宝典

Spring实战 | Spring IOC不能说的秘密?

国庆中秋特辑系列文章:

国庆中秋特辑(八)Spring Boot项目如何使用JPA

国庆中秋特辑(七)Java软件工程师常见20道编程面试题

国庆中秋特辑(六)大学生常见30道宝藏编程面试题

国庆中秋特辑(五)MySQL如何性能调优?下篇

国庆中秋特辑(四)MySQL如何性能调优?上篇

国庆中秋特辑(三)使用生成对抗网络(GAN)生成具有节日氛围的画作,深度学习框架 TensorFlow 和 Keras 来实现

国庆中秋特辑(二)浪漫祝福方式 使用生成对抗网络(GAN)生成具有节日氛围的画作

国庆中秋特辑(一)浪漫祝福方式 用循环神经网络(RNN)或长短时记忆网络(LSTM)生成祝福诗词

在这里插入图片描述

Redis 的有序集合(Sorted Set)是一个基于分数(score)排序的数据结构,它在 Redis 中非常重要,常用于实现排行榜、近似计数器等功能。

Redis 的有序集合(Sorted Set)是基于跳跃表(Skip List)实现的。跳跃表是一种高效的数据结构,其插入、删除和查找操作的平均时间复杂度都是 O(log n),相对于平衡树(如红黑树)的实现要简单很多。跳跃表的结构类似于链表,每个节点除了保存元素值外,还包含一个指针数组,分别指向对应层次的下一个节点。这种多级指针的设计,使得跳表可以跨越多个节点进行快速搜索,同时保证跳表结构的高效性和简洁性。
有序集合的底层数据结构由哈希(Hash)和跳跃表组成。在哈希中,存储了元素及其关联的评分(分数)。每个元素都有一个唯一的评分,用于确定其在跳跃表中的位置。当需要对有序集合进行操作时,Redis 首先通过哈希表找到元素及其评分,然后通过跳跃表进行相应的操作。
以下是 Redis 有序集合(Sorted Set)的一些核心操作及其对应的核心代码分析:

  1. 添加元素(ZADD):
    有序集合中的元素添加操作是通过哈希表和跳跃表协同完成的。首先,Redis 将元素值和评分存储在哈希表中。然后,根据评分在跳跃表中找到对应的位置,并将新元素插入到该位置。
  2. 获取元素(ZRANGE、ZREVRANGE):
    有序集合中的获取元素操作主要依赖于跳跃表。ZRANGE 操作从跳跃表的头部开始,按照给定的评分范围返回符合条件的元素。ZREVRANGE 操作则从跳跃表的尾部开始,按照给定的评分范围返回符合条件的元素。
  3. 删除元素(ZREM):
    删除元素操作首先通过哈希表找到对应元素,然后在跳跃表中删除该元素。Redis 只需要删除哈希表中的指向该元素的指针,跳跃表中的元素会自动上移。
  4. 更新元素评分(ZINCRBY):
    更新元素评分操作仅需修改哈希表中对应元素的评分,然后重新计算跳跃表中元素的位置。
  5. 获取有序集合长度(ZCARD):
    有序集合长度的操作直接查询哈希表中的键值对数量。
  6. 随机获取元素(ZRANDMEMBER):
    随机获取元素操作首先从哈希表中随机选择一个元素,然后在该元素所在的跳跃表区间内随机选择一个元素。
    通过以上操作,Redis 实现了高效有序集合(Sorted Set)的数据结构,提供了高性能的排序和范围查找功能。

2、实战

要使用 Spring Boot 和 Redis 实现排行榜功能,你可以遵循以下步骤:

  1. 引入依赖
    在你的 Spring Boot 项目的 pom.xml 文件中,添加以下依赖:
<dependencies>  <dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-data-redis</artifactId>  </dependency>  
</dependencies>  
  1. 配置 Redis
    在 application.properties 或 application.yml 文件中配置 Redis 连接信息:
# application.properties  
spring.redis.host=localhost  
spring.redis.port=6379  
# application.yml  
spring:  redis:  host: localhost  port: 6379  
  1. 创建 Redis 模板
    创建一个 RedisTemplate Bean:
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.data.redis.connection.RedisConnectionFactory;  
import org.springframework.data.redis.core.RedisTemplate;  
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;  
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration  
public class RedisConfig {@Bean  public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {  RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();  redisTemplate.setConnectionFactory(redisConnectionFactory);  redisTemplate.setKeySerializer(new StringRedisSerializer());  redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());  redisTemplate.setHashKeySerializer(new StringRedisSerializer());  redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());  return redisTemplate;  }  
}
  1. 创建排行榜实体类
    创建一个排行榜实体类,包含用户 ID、分数等信息:
import java.io.Serializable;
public class RankingEntity implements Serializable {private String userId;  private double score;// 构造方法、getter 和 setter  
}
  1. 实现 Redis 排行榜操作
    创建一个服务类,实现排行榜的相关操作,如添加分数、查询排名等:
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.data.redis.core.RedisTemplate;  
import org.springframework.data.redis.core.ValueOperations;  
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service  
public class RankingService {@Autowired  private RedisTemplate<String, Object> redisTemplate;private static final String RANKING_KEY = "ranking_list";/**  * 添加分数  * @param userId 用户 ID  * @param score 分数  */  public void addScore(String userId, double score) {  ValueOperations<String, RankingEntity> valueOperations = redisTemplate.opsForValue();  valueOperations.set(RANKING_KEY + ":" + userId, score, 60, TimeUnit.SECONDS);  }/**  * 查询排名  * @return 排名列表  */  public List<Object> getRankingList() {  List<Object> rankingList = redisTemplate.opsForList().range(RANKING_KEY, 0, -1);  return rankingList;  }/**  * 查询用户排名  * @param userId 用户 ID  * @return 用户排名  */  public Object getUserRanking(String userId) {  return redisTemplate.opsForValue().get(RANKING_KEY + ":" + userId);  }* @param userId 用户 ID  */  public void deleteUserScore(String userId) {  ValueOperations<String, RankingEntity> valueOperations = redisTemplate.opsForValue();  valueOperations.delete(RANKING_KEY + ":" + userId);  }/**  * 更新用户分数  * @param userId 用户 ID  * @param score 新的分数  */  public void updateUserScore(String userId, double score) {  ValueOperations<String, RankingEntity> valueOperations = redisTemplate.opsForValue();  valueOperations.set(RANKING_KEY + ":" + userId, score, 60, TimeUnit.SECONDS);  }/**  * 获取用户排名列表的长度  * @return 用户排名列表的长度  */  public long getUserRankingListSize() {  return redisTemplate.opsForList().size(RANKING_KEY);  }/**  * 在用户排名列表中插入用户分数  * @param userId 用户 ID  * @param score 分数  * @param index 插入位置,0 表示插入到列表头部,负数表示插入到列表尾部  */  public void insertUserScore(String userId, double score, long index) {  List<Object> rankingList = redisTemplate.opsForList().range(RANKING_KEY, 0, -1);  redisTemplate.opsForList().leftPush(RANKING_KEY, score, index);  }/**  * 在用户排名列表中删除用户分数  * @param userId 用户 ID  * @param index 删除位置,0 表示删除第一个元素,1 表示删除第二个元素,依此类推  */  public void deleteUserScore(String userId, long index) {  List<Object> rankingList = redisTemplate.opsForList().range(RANKING_KEY, 0, -1);  redisTemplate.opsForList().rightPop(RANKING_KEY, index);  }/**  * 获取用户排名列表中的最后一个元素  * @return 用户排名列表中的最后一个元素  */  public Object getLastUserScore() {  return redisTemplate.opsForList().rightPop(RANKING_KEY);  }/**  * 获取用户排名列表中的第一个元素  * @return 用户排名列表中的第一个元素  */  public Object getFirstUserScore() {  return redisTemplate.opsForList().leftPop(RANKING_KEY);  }/**  * 在用户排名列表中添加元素  * @param score 添加的分数  */  public void addUserScore(double score) {  redisTemplate.opsForList().rightPush(RANKING_KEY, score);  }/**  * 在用户排名列表中删除元素  * @param index 删除位置,0 表示删除第一个元素,1 表示删除第二个元素,依此类推  */  public void deleteUserScore(long index) {  redisTemplate.opsForList().rightPop(RANKING_KEY, index);  }/**  * 获取用户排名列表  * @return 用户排名列表  */  public List<Object> getUserRankingList() {  return redisTemplate.opsForList().range(RANKING_KEY, 0, -1);  }/**  * 设置用户排名列表的长度  * @param length 用户排名列表的新长度  */  public void setUserRankingListLength(long length) {  redisTemplate.opsForList().setSize(RANKING_KEY, length);  }/**  * 获取用户排名  *  * @param userId 用户 ID  * @return 用户排名,如果用户不存在,返回 -1  */  public int getUserRanking(String userId) {  List<Object> rankingList = getRankingList();  Object userScore = getUserRanking(userId);  if (userScore == null) {  return -1;  }int rank = 0;    for (Object score : rankingList) {    if (score.equals(userScore)) {    break;    }    rank++;    }    return rank;    }/**  * 获取用户排名列表中的指定位置的元素  *  * @param index 指定位置,从 0 开始  * @return 用户排名列表中的指定位置的元素  */  public Object getUserRankingListElement(long index) {  return redisTemplate.opsForList().range(RANKING_KEY, 0, -1).get(index);  }/**  * 获取用户排名列表中的用户分数  *  * @param userId 用户 ID  * @return 用户排名列表中的用户分数,如果用户不存在,返回 null  */  public Object getUserRanking(String userId) {  ValueOperations<String, RankingEntity> valueOperations = redisTemplate.opsForValue();  return valueOperations.get(RANKING_KEY + ":" + userId);  }/**  * 是否存在用户  *  * @param userId 用户 ID  * @return 是否存在用户  */  public boolean existsUser(String userId) {  ValueOperations<String, RankingEntity> valueOperations = redisTemplate.opsForValue();  return valueOperations.hasKey(RANKING_KEY + ":" + userId);  }/**  * 清除所有用户排名数据  */  public void clearAllUserRankingData() {  redisTemplate.delete(RANKING_KEY);  }  
}

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

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

相关文章

网络通信——与Socket交换数据(三十一)

1. 与Socket交换数据 1.1 知识点 &#xff08;1&#xff09;通过Android与Socket完成基本的Echo程序实现&#xff1b; &#xff08;2&#xff09;通过对象序列化进行大数据的传输&#xff1b; 1.2 具体内容 对于网络的开发而言&#xff0c;最常使用的交互模式&#xff1a;W…

算法之路(一)

&#x1f58a;作者 : D. Star. &#x1f4d8;专栏 :算法小能手 &#x1f606;今日分享 : 如何学习&#xff1f; 在学习的过程中&#xff0c;不仅要知道如何学习&#xff0c;还要知道避免学习的陷阱。1. 睡眠不足&#xff1b;2. 被动学习和重读&#xff1b;3. 强调标记或画线&am…

基于单片机的甲醛检测器设计

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 技术交流认准下方 CSDN 官方提供的联系方式 文章目录 概要 一、设计的主要内容二、系统硬件设计三、软件设计4.1 程序结构流程图原理图 四、结论五、 文章目录 概要 本文将要提…

【k8s】pod控制器

一、pod控制器及其功用 Pod是kubernetes的最小管理单元&#xff0c;在kubernetes中&#xff0c;按照Pod的创建方式可以将其分为两类 自主式Pod&#xff1a; kubernetes直接创建出来的Pod&#xff0c;这种Pod删除后就没有了&#xff0c;也不会重建 控制器创建的Pod&#xff1a…

2023.11.7 Spring 依赖注入的三大方式

目录 前言 属性注入&#xff08;Autowired&#xff09; Setter 注入 构造方法注入 Resource Autowired 和 Resource 的区别 Autowired 和 Resource 查找 Bean 对象的区别 前言 配置文件 ​ <?xml version"1.0" encoding"UTF-8"?> <beans …

uniapp在不需要后端数据的情况下 怎么记录用户进一次记录一次

目录 前言&#xff1a; html部分 js部分 完整代码 前言&#xff1a; 一时兴起&#xff0c;不喜勿喷&#xff0c;今天听到了这个问题想到了一个方法&#xff0c;解决方式如下。 html部分 他用于显示访问次数&#xff08;visitCount变量的值&#xff09;。 <template&…

MySQL主从搭建,实现读写分离(基于docker)

一 主从配置原理 mysql主从配置的流程大体如图&#xff1a; 1&#xff09;master会将变动记录到二进制日志里面&#xff1b; 2&#xff09;master有一个I/O线程将二进制日志发送到slave; 3) slave有一个I/O线程把master发送的二进制写入到relay日志里面&#xff1b; 4&#xf…

万字长文 - Python 日志记录器logging 百科全书 之 基础配置

万字长文 - Python 日志记录器logging 百科全书 之 基础配置 前言 在日常的开发中工作中&#xff0c;日志记录扮演着不可或缺的角色。它不仅能让我们了解应用程序的运行状况&#xff0c;还能帮助我们定位并解决各种问题。 最基本的&#xff0c;它记录了应用程序的运行情况&am…

SBCFormer:快速、轻量级神经网络 for 边缘AI

一&#xff1a; SBCFormer [ PDF] SBCFormer 是一种 CNN-ViT 轻量混合网络backbone&#xff0c;能在单板计算机上能以每秒 1 帧速度进行全尺寸 ImageNet 分类的轻量级网络。 通过在低端 CPU 上实现高准确性和快速计算&#xff0c;为树莓派 4 型号 B 的 ARM-Cortex A72 CPU 提供…

情感分析与LLMs角色扮演

深度学习自然语言处理 原创作者&#xff1a;wkk 就像人类在做一件事情的时候&#xff0c;可能需要尝试多次。LLM也是如此&#xff01;这对于情感分析任务尤其如此&#xff0c;在情感分析任务中&#xff0c;LLM需要深入推理来处理输入中的复杂语言现象&#xff08;例如&#xff…

「Qt Widget中文示例指南」如何模拟一个时钟?

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写&#xff0c;所有平台无差别运行&#xff0c;更提供了几乎所有开发过程中需要用到的工具。如今&#xff0c;Qt已被运用于超过70个行业、数千家企业&#xff0c;支持数百万设备及应用。 点击获取Qt Widget组…

康耐视深度学习ViDi-ViDi四大工具之一蓝色定位工具/Locate

目录 工具介绍使用步骤说明调整工具ROI添加特征标签生成定位姿态训练并审核模型编辑器参数说明蓝色定位工具/Locate工具 工具介绍 蓝色定位工具用于识别和定位图像中的特定特征或特征组。该工具的输出可用于为其他ViDi 工具提供位置数据。使用该工具时,您提供图像训练集,然后…

Apache Doris (五十二): Doris Join类型 - Broadcast Join

🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹哥教你大数据个人主页-哔哩哔哩视频 目录 1. Broadcast Join原理

Vue+OpenLayers 创建地图并显示鼠标所在经纬度

1、效果 2、创建地图 本文用的是高德地图 页面 <div class"map" id"map"></div><div id"mouse-position" class"position_coordinate"></div>初始化地图 var gaodeLayer new TileLayer({title: "高德地…

python数据分析及可视化(十五)数据分析可视化实战篇(抖音用户数据分析、二手房数据分析)

python数据分析的实战篇&#xff0c;围绕实例的数据展开分析&#xff0c;通过数据操作案例来了解数据分析中的频繁用到的知识内容。 抖音用户数据分析 1.理解数据 数据字段含义 了解数据内容&#xff0c;确保数据来源是正常的&#xff0c;安全合法的。理解一下每一个字段的…

C站你好,和你相遇的第1825天

文章目录 机缘收获日常成就憧憬 机缘 ①. 你好,C站 ②. 初识JAVA编程,遇到问题,粘贴问题百度搜索,大都数出来的解决方案都能在C站得到解决,对C站有一定的好感 ③. 起初在CSDN写博客,主要用来记录日常学习过程中的笔记、不断调整自己的笔记,如JAVA基础、框架、虚拟机等,为后…

java传base64返回给数据报404踩坑

一、问题复现 1.可能因为base64字符太长&#xff0c;导致后端处理时出错&#xff0c;表现为前端请求报400错误&#xff1b; 这一步debug进去发现base64数据是正常传值的 所以排除掉不是后端问题,但是看了下前端请求,猜测可能是转换base64时间太长数据过大导致的404 2.前端传…

聚观早报 |GPT-4周活用户数达1亿;长城汽车10月销量增加

【聚观365】11月8日消息 GPT-4周活用户数达1亿 长城汽车10月销量增加 xAI宣布推出PromptIDE工具 aigo爱国者连发5款儿童手表 SpaceX预计今年营收90亿美元 GPT-4周活用户数达1亿 在OpenAI首届开发者大会上&#xff0c;该公司首席执行官萨姆奥特曼&#xff08;Sam Altman&a…

删除word最后一页之后的空白页

最近编辑word比较多&#xff0c;有时最后一页&#xff08;最后一页内容还有可能是表格&#xff09;之后&#xff0c;还有一页空白页&#xff0c;单独按下backspace、del都删不掉&#xff0c;很让人着急。 经过查询有几种方法&#xff1a; &#xff08;1&#xff09;点击选中空…

C#中基于.NET6的动态编译技术

前几天要解决动态计算问题&#xff0c;尝试着使用了不同的方法。问题是给定一个包含计算的字符串&#xff0c;在程序运行中得到计算结果&#xff0c;当时考虑了动态编译&#xff0c;在网上查了一些资料完成了这项功能&#xff0c;可是基于不同的.NET平台使用的编程代码相差比较…