0


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:
importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.data.redis.connection.RedisConnectionFactory;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;importorg.springframework.data.redis.serializer.StringRedisSerializer;@ConfigurationpublicclassRedisConfig{@BeanpublicRedisTemplate<String,Object>redisTemplate(RedisConnectionFactory redisConnectionFactory){RedisTemplate<String,Object> redisTemplate =newRedisTemplate<>();  
        redisTemplate.setConnectionFactory(redisConnectionFactory);  
        redisTemplate.setKeySerializer(newStringRedisSerializer());  
        redisTemplate.setValueSerializer(newGenericJackson2JsonRedisSerializer());  
        redisTemplate.setHashKeySerializer(newStringRedisSerializer());  
        redisTemplate.setHashValueSerializer(newGenericJackson2JsonRedisSerializer());return redisTemplate;}}
  1. 创建排行榜实体类 创建一个排行榜实体类,包含用户 ID、分数等信息:
importjava.io.Serializable;publicclassRankingEntityimplementsSerializable{privateString userId;privatedouble score;// 构造方法、getter 和 setter  }
  1. 实现 Redis 排行榜操作 创建一个服务类,实现排行榜的相关操作,如添加分数、查询排名等:
importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.data.redis.core.ValueOperations;importorg.springframework.stereotype.Service;importjava.util.concurrent.TimeUnit;@ServicepublicclassRankingService{@AutowiredprivateRedisTemplate<String,Object> redisTemplate;privatestaticfinalStringRANKING_KEY="ranking_list";/**  
     * 添加分数  
     * @param userId 用户 ID  
     * @param score 分数  
     */publicvoidaddScore(String userId,double score){ValueOperations<String,RankingEntity> valueOperations = redisTemplate.opsForValue();  
        valueOperations.set(RANKING_KEY+":"+ userId, score,60,TimeUnit.SECONDS);}/**  
     * 查询排名  
     * @return 排名列表  
     */publicList<Object>getRankingList(){List<Object> rankingList = redisTemplate.opsForList().range(RANKING_KEY,0,-1);return rankingList;}/**  
     * 查询用户排名  
     * @param userId 用户 ID  
     * @return 用户排名  
     */publicObjectgetUserRanking(String userId){return redisTemplate.opsForValue().get(RANKING_KEY+":"+ userId);}*@param userId 用户 ID*/publicvoiddeleteUserScore(String userId){ValueOperations<String,RankingEntity> valueOperations = redisTemplate.opsForValue();  
        valueOperations.delete(RANKING_KEY+":"+ userId);}/**  
     * 更新用户分数  
     * @param userId 用户 ID  
     * @param score 新的分数  
     */publicvoidupdateUserScore(String userId,double score){ValueOperations<String,RankingEntity> valueOperations = redisTemplate.opsForValue();  
        valueOperations.set(RANKING_KEY+":"+ userId, score,60,TimeUnit.SECONDS);}/**  
     * 获取用户排名列表的长度  
     * @return 用户排名列表的长度  
     */publiclonggetUserRankingListSize(){return redisTemplate.opsForList().size(RANKING_KEY);}/**  
     * 在用户排名列表中插入用户分数  
     * @param userId 用户 ID  
     * @param score 分数  
     * @param index 插入位置,0 表示插入到列表头部,负数表示插入到列表尾部  
     */publicvoidinsertUserScore(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 表示删除第二个元素,依此类推  
     */publicvoiddeleteUserScore(String userId,long index){List<Object> rankingList = redisTemplate.opsForList().range(RANKING_KEY,0,-1);  
        redisTemplate.opsForList().rightPop(RANKING_KEY, index);}/**  
     * 获取用户排名列表中的最后一个元素  
     * @return 用户排名列表中的最后一个元素  
     */publicObjectgetLastUserScore(){return redisTemplate.opsForList().rightPop(RANKING_KEY);}/**  
     * 获取用户排名列表中的第一个元素  
     * @return 用户排名列表中的第一个元素  
     */publicObjectgetFirstUserScore(){return redisTemplate.opsForList().leftPop(RANKING_KEY);}/**  
     * 在用户排名列表中添加元素  
     * @param score 添加的分数  
     */publicvoidaddUserScore(double score){  
        redisTemplate.opsForList().rightPush(RANKING_KEY, score);}/**  
     * 在用户排名列表中删除元素  
     * @param index 删除位置,0 表示删除第一个元素,1 表示删除第二个元素,依此类推  
     */publicvoiddeleteUserScore(long index){  
        redisTemplate.opsForList().rightPop(RANKING_KEY, index);}/**  
     * 获取用户排名列表  
     * @return 用户排名列表  
     */publicList<Object>getUserRankingList(){return redisTemplate.opsForList().range(RANKING_KEY,0,-1);}/**  
     * 设置用户排名列表的长度  
     * @param length 用户排名列表的新长度  
     */publicvoidsetUserRankingListLength(long length){  
        redisTemplate.opsForList().setSize(RANKING_KEY, length);}/**  
     * 获取用户排名  
     *  
     * @param userId 用户 ID  
     * @return 用户排名,如果用户不存在,返回 -1  
     */publicintgetUserRanking(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 用户排名列表中的指定位置的元素  
     */publicObjectgetUserRankingListElement(long index){return redisTemplate.opsForList().range(RANKING_KEY,0,-1).get(index);}/**  
     * 获取用户排名列表中的用户分数  
     *  
     * @param userId 用户 ID  
     * @return 用户排名列表中的用户分数,如果用户不存在,返回 null  
     */publicObjectgetUserRanking(String userId){ValueOperations<String,RankingEntity> valueOperations = redisTemplate.opsForValue();return valueOperations.get(RANKING_KEY+":"+ userId);}/**  
     * 是否存在用户  
     *  
     * @param userId 用户 ID  
     * @return 是否存在用户  
     */publicbooleanexistsUser(String userId){ValueOperations<String,RankingEntity> valueOperations = redisTemplate.opsForValue();return valueOperations.hasKey(RANKING_KEY+":"+ userId);}/**  
     * 清除所有用户排名数据  
     */publicvoidclearAllUserRankingData(){  
        redisTemplate.delete(RANKING_KEY);}}

本文转载自: https://blog.csdn.net/superdangbo/article/details/134236814
版权归原作者 Web3&Basketball 所有, 如有侵权,请联系我们删除。

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

还没有评论