0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

使用Redis的有序集合实现排行榜功能和Spring Boot集成

马哥Linux运维 来源:csdn 2023-11-20 10:15 次阅读

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

Redis 的有序集合(Sorted Set)是基于跳跃表(Skip List)实现的。跳跃表是一种高效的数据结构,其插入、删除和查找操作的平均时间复杂度都是 O(log n),相对于平衡树(如红黑树)的实现要简单很多。跳跃表的结构类似于链表,每个节点除了保存元素值外,还包含一个指针数组,分别指向对应层次的下一个节点。这种多级指针的设计,使得跳表可以跨越多个节点进行快速搜索,同时保证跳表结构的高效性和简洁性。

有序集合的底层数据结构由哈希(Hash)和跳跃表组成。在哈希中,存储了元素及其关联的评分(分数)。每个元素都有一个唯一的评分,用于确定其在跳跃表中的位置。当需要对有序集合进行操作时,Redis 首先通过哈希表找到元素及其评分,然后通过跳跃表进行相应的操作。

以下是 Redis 有序集合(Sorted Set)的一些核心操作及其对应的核心代码分析:

添加元素(ZADD):

有序集合中的元素添加操作是通过哈希表和跳跃表协同完成的。首先,Redis 将元素值和评分存储在哈希表中。然后,根据评分在跳跃表中找到对应的位置,并将新元素插入到该位置。

获取元素(ZRANGE、ZREVRANGE):

有序集合中的获取元素操作主要依赖于跳跃表。ZRANGE 操作从跳跃表的头部开始,按照给定的评分范围返回符合条件的元素。ZREVRANGE 操作则从跳跃表的尾部开始,按照给定的评分范围返回符合条件的元素。

删除元素(ZREM):

删除元素操作首先通过哈希表找到对应元素,然后在跳跃表中删除该元素。Redis 只需要删除哈希表中的指向该元素的指针,跳跃表中的元素会自动上移。

更新元素评分(ZINCRBY):

更新元素评分操作仅需修改哈希表中对应元素的评分,然后重新计算跳跃表中元素的位置。

获取有序集合长度(ZCARD):

有序集合长度的操作直接查询哈希表中的键值对数量。

随机获取元素(ZRANDMEMBER):

随机获取元素操作首先从哈希表中随机选择一个元素,然后在该元素所在的跳跃表区间内随机选择一个元素。

通过以上操作,Redis 实现了高效有序集合(Sorted Set)的数据结构,提供了高性能的排序和范围查找功能。

2、实战

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

引入依赖

在你的 Spring Boot 项目的 pom.xml 文件中,添加以下依赖:

  
      
        org.springframework.boot  
        spring-boot-starter-data-redis  
      
 

配置 Redis

在 application.properties 或 application.yml 文件中配置 Redis 连接信息

# application.properties  
spring.redis.host=localhost  
spring.redis.port=6379  
# application.yml  
spring:  
  redis:  
    host: localhost  
    port: 6379  

创建 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 redisTemplate(RedisConnectionFactory redisConnectionFactory) {  
        RedisTemplate redisTemplate = new RedisTemplate<>();  
        redisTemplate.setConnectionFactory(redisConnectionFactory);  
        redisTemplate.setKeySerializer(new StringRedisSerializer());  
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());  
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());  
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());  
        return redisTemplate;  
    }  
}

创建排行榜实体类

创建一个排行榜实体类,包含用户 ID、分数等信息:

import java.io.Serializable;
public class RankingEntity implements Serializable {
    private String userId;  
    private double score;
    // 构造方法、getter 和 setter 

实现 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 redisTemplate;
    private static final String RANKING_KEY = "ranking_list";
    /**  
     * 添加分数  
     * @param userId 用户 ID  
     * @param score 分数  
     */  
    public void addScore(String userId, double score) {  
        ValueOperations valueOperations = redisTemplate.opsForValue();  
        valueOperations.set(RANKING_KEY + ":" + userId, score, 60, TimeUnit.SECONDS);  
    }
    /**  
     * 查询排名  
     * @return 排名列表  
     */  
    public List getRankingList() {  
        List 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 valueOperations = redisTemplate.opsForValue();  
        valueOperations.delete(RANKING_KEY + ":" + userId);  
    }
    /**  
     * 更新用户分数  
     * @param userId 用户 ID  
     * @param score 新的分数  
     */  
    public void updateUserScore(String userId, double score) {  
        ValueOperations 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 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 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 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 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 valueOperations = redisTemplate.opsForValue();  
        return valueOperations.get(RANKING_KEY + ":" + userId);  
    }
    /**  
     * 是否存在用户  
     *  
     * @param userId 用户 ID  
     * @return 是否存在用户  
     */  
    public boolean existsUser(String userId) {  
        ValueOperations valueOperations = redisTemplate.opsForValue();  
        return valueOperations.hasKey(RANKING_KEY + ":" + userId);  
    }
    /**  
     * 清除所有用户排名数据  
     */  
    public void clearAllUserRankingData() {  
        redisTemplate.delete(RANKING_KEY);  
    }  
}







审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 计数器
    +关注

    关注

    32

    文章

    2121

    浏览量

    92976
  • Redis
    +关注

    关注

    0

    文章

    362

    浏览量

    10492

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

文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    中国IC设计公司排行榜

    作者:林晓林  中国IC设计公司排行榜:近日,市场调研公司iSuppli出台了2005年度中国IC设计公司排行榜,与中国半导体协会的排名不同,此次名列榜首的是来自香港的晶门
    发表于 05-26 14:29

    2013年2月份编程软件排行榜,LabVIEWTop27,进步很大。

    本帖最后由 sushu 于 2013-2-13 10:58 编辑 刚刚上网发现编程软件排名,关注的LabVIEW现在已经是27位了,小开心一下。TIOBE编程语言社区排行榜是编程语言流行趋势
    发表于 11-06 12:40

    资料下载总排行榜

    资料下载总排行榜,怎么就那几个啊,怎么下载到人气高的资料?资料茫茫,我相信大家的眼睛雪亮的。求方法收集些好的资料。。。
    发表于 03-05 16:24

    各种排行榜汇总贴!!!!!

    本帖最后由 dongyumin 于 2013-7-31 11:39 编辑 1.2012网上各地年终奖排行榜,科技、电子企业全面领跑!https://bbs.elecfans.com
    发表于 07-30 11:55

    2014年10月 TIOBE 编程语言排行榜发布

    2014年10月的 TIOBE 编程语言排行榜发布了,该版本最大的两点是 Google 的 Dart 语言首次进入前 20 名。其竞争者包括 CoffeeScript 目前排名 133,TypeScript 排名 122.想知道完整的排名表格请回复
    发表于 12-08 13:46

    小米放出“手机电量排行榜” 为续航神机Max 2造势

    小米手机家族的电量排行榜,并向网友征询:“你觉得小米Max2多大电量够你用? ”从排行榜来看,现款小米Max以4850mAh的容量排名第一,其次是小米MIX(4400mAh)、红米4(4100mAh
    发表于 06-03 14:20

    Redis有序集合详细步骤

    利用Redis Sorted Set实现排行榜功能
    发表于 05-21 14:09

    MapReduce框架音乐排行榜案例

    Hadoop综合实战之MapReduce运算优化——音乐排行榜
    发表于 10-16 12:20

    Spring bootRedis的使用

    【本人秃顶程序员】springboot专辑:Spring bootRedis的使用
    发表于 03-27 11:42

    求职必知独角兽公司排行榜

    世界第 3 的滴滴裁员,求职必知独角兽公司排行榜
    发表于 06-18 07:30

    2019年2月编程语言排行榜分享

    2019年2月编程语言排行榜
    发表于 07-14 10:28

    2020年最新主板型号排行榜 精选资料推荐

    2020年最新主板型号排行榜2020主板型号天梯图2020主板选购指南一、Intel、AMD电脑主板的辨别二、主板芯片组级别三、板形四、主板对电脑性能有什么影响在使用电脑的时候,我们有时候会需要更换
    发表于 07-26 06:16

    玩转Redis-使用有序集合(sorted sets)实现投票游戏

    存储kv的数据库,合理的使用redis作为缓存,可以极大的改善系统的性能和服务器请求响应时间。redis除了基本的kv存储以外,还实现了哈希(Map), 列表(list), 集合(se
    的头像 发表于 09-26 12:40 2376次阅读

    基于排行榜功能使用redis中的有序集合实现

    排行榜功能是一个很普遍的需求,对于这类需求如果数据总量过大用mysql实现的话会很浪费性能。
    的头像 发表于 10-21 09:59 788次阅读

    Redis的常用场景有哪些

    策略,所以,现在Redis用在缓存的场合非常多。 2、排行榜 很多网站都有排行榜应用的,如京东的月度销量榜单、商品按时间的上新排行榜等。Redis
    的头像 发表于 10-09 10:44 338次阅读