【Redis从头学-8】Redis中的ZSet数据类型实战场景之用户积分榜

‍作者名称:DaenCode
作者简介:啥技术都喜欢捣鼓捣鼓,喜欢分享技术、经验、生活。
人生感悟:尝尽人生百味,方知世间冷暖。
所属专栏:Redis从头学


【Redis从头学-8】Redis中的ZSet数据类型实战场景之用户积分榜_第1张图片


文章目录

  • 前言
  • ZSet数据类型分析
  • ZSet类型实战应用场景
    • 用户积分榜功能
      • 代码示例
      • 数据测试
      • 运行结果
  • 写在最后

前言

之前的篇章对Redis的String、List、Hash、Set数据类型已经做出了具体分析,并举例说明了其具体的实战场景。本文就结合Zset数据类型结构的特性,一起探讨其实战中的应用场景,并以积分榜功能为例来展示Zset数据类型的特点。

ZSet数据类型分析

Redis中的ZSet(有序集合)数据类型是一种有序且不重复的集合,它在Set的基础上增加了一个分数(score)字段,用于对集合中的元素进行排序。下面对Redis ZSet数据类型进行一些分析:

  1. 有序性:ZSet中的元素按照其分数进行排序,使得元素在集合中有序存储。每个元素都有一个唯一的分数,可用于根据指定顺序进行范围查询或排序。
  2. 元素的唯一性:和Set一样,ZSet保证其中的元素都是唯一的,不会存在重复的元素。
  3. 高效的添加、删除和更新操作:ZSet提供了O(log N)时间复杂度的添加、删除和更新元素的操作。其中N为ZSet中元素的数量。这归功于Redis内部使用了跳表(Skip List)和哈希表两种结构实现ZSet。
  4. 支持范围查询和排名操作:ZSet支持根据分数范围进行查询,并可以按照分数大小对元素进行排名。通过排名操作,可以获取元素的排名以及根据排名返回一定范围的元素。

ZSet类型实战应用场景

ZSet常用于需要根据分数进行排序的场景,例如排行榜、计分系统、有序任务队列等。它能够快速获取按照分数排序的元素,并且支持动态更新分数。

总而言之,Redis的ZSet数据类型提供了有序、唯一且高效的集合操作。它在排行榜、计分系统以及需要有序处理任务队列等场景中非常有用。通过对元素进行分数的设置和操作,可以灵活地满足各种实时数据排序和查询的需求。

用户积分榜功能

代码示例

我们使用了Spring Data Redis提供的RedisTemplate来操作Redis的ZSet。通过@Resource注解将RedisTemplate注入到LeaderboardService类中。

在LeaderboardService中,我们定义很多的功能方法来实现用户积分榜的功能,如添加用户积分、增加用户积分、获取用户排名、获取用户积分、获取排名靠前的用户列表以及获取积分在指定范围内的用户列表。

@Component
public class LeaderboardService {

    private static final String LEADERBOARD_KEY = "leaderboard";

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 添加用户积分
     *
     * @param user  用户名
     * @param score 积分
     */
    public void addScore(String user, double score) {
        ZSetOperations<String, String> zSetOperations = redisTemplate.opsForZSet();
        zSetOperations.add(LEADERBOARD_KEY, user, score);
    }

    /**
     * 增加用户积分
     *
     * @param user  用户名
     * @param score 积分增加量
     */
    public void incrementScore(String user, double score) {
        ZSetOperations<String, String> zSetOperations = redisTemplate.opsForZSet();
        zSetOperations.incrementScore(LEADERBOARD_KEY, user, score);
    }

    /**
     * 获取用户排名(从高到低)
     *
     * @param user 用户名
     * @return 用户的排名,如果用户不存在,则返回null
     */
    public Long getUserRank(String user) {
        ZSetOperations<String, String> zSetOperations = redisTemplate.opsForZSet();
        return zSetOperations.reverseRank(LEADERBOARD_KEY, user);
    }

    /**
     * 获取用户积分
     *
     * @param user 用户名
     * @return 用户的积分,如果用户不存在,则返回null
     */
    public Double getUserScore(String user) {
        ZSetOperations<String, String> zSetOperations = redisTemplate.opsForZSet();
        return zSetOperations.score(LEADERBOARD_KEY, user);
    }

    /**
     * 获取排名靠前的用户列表
     *
     * @param count 列表数量
     * @return 排名靠前的用户列表
     */
    public Set<String> getTopUsers(int count) {
        ZSetOperations<String, String> zSetOperations = redisTemplate.opsForZSet();
        Set<String> topUsers = zSetOperations.reverseRange(LEADERBOARD_KEY, 0, count - 1);
        return topUsers;
    }

    /**
     * 获取积分在指定范围内的用户列表
     *
     * @param minScore 最低积分
     * @param maxScore 最高积分
     * @return 积分在指定范围内的用户列表
     */
    public Set<String> getUsersInRange(double minScore, double maxScore) {
        ZSetOperations<String, String> zSetOperations = redisTemplate.opsForZSet();
        Set<String> usersInRange = zSetOperations.rangeByScore(LEADERBOARD_KEY, minScore, maxScore);
        return usersInRange;
    }

    /**
     * 获取积分在指定范围内的用户列表,并返回用户及其对应的积分信息
     *
     * @param minScore 最低积分
     * @param maxScore 最高积分
     * @return 包含用户及其对应积分的用户列表
     */
    public Set<String> getUsersWithScoresInRange(double minScore, double maxScore) {
        ZSetOperations<String, String> zSetOperations = redisTemplate.opsForZSet();
        Set<ZSetOperations.TypedTuple<String>> usersWithScoresInRange = zSetOperations
                .rangeByScoreWithScores(LEADERBOARD_KEY, minScore, maxScore);
        // 将TypedTuple转换为只包含用户的Set
        Set<String> usersSet = usersWithScoresInRange.stream()
                .map(ZSetOperations.TypedTuple::getValue)
                .collect(Collectors.toSet());
        return usersSet;
    }

}

数据测试

使用了 Spring Boot 框架来启动应用程序,并通过上下文获取 LeaderboardService 类的实例。然后,我们按照需求调用 LeaderboardService 类中的方法。

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
        LeaderboardService leaderboardService = context.getBean(LeaderboardService.class);

        // 添加用户积分
        leaderboardService.addScore("User1", 100);
        leaderboardService.addScore("User2", 200);
        leaderboardService.addScore("User3", 300);
        leaderboardService.addScore("User4", 400);
        leaderboardService.addScore("User5", 500);

        // 增加用户积分
        leaderboardService.incrementScore("User1", 50);
        leaderboardService.incrementScore("User3", 150);

        // 获取用户排名
        Long user1Rank = leaderboardService.getUserRank("User1");
        System.out.println("User1 Rank: " + user1Rank);

        // 获取用户积分
        Double user3Score = leaderboardService.getUserScore("User3");
        System.out.println("User3 Score: " + user3Score);

        // 获取排名靠前的用户列表
        Set<String> topUsers = leaderboardService.getTopUsers(3);
        System.out.println("Top Users: " + topUsers);

        // 获取积分在指定范围内的用户列表
        Set<String> usersInRange = leaderboardService.getUsersInRange(200, 400);
        System.out.println("Users in Range: " + usersInRange);

        // 获取积分在指定范围内的用户列表,并返回用户及其对应的积分信息
        Set<String> usersWithScoresInRange = leaderboardService.getUsersWithScoresInRange(200, 400);
        System.out.println("Users with Scores in Range: " + usersWithScoresInRange);
    }
}

运行结果

User1 Rank: 4
User3 Score: 450.0
Top Users: [User5, User4, User3]
Users in Range: [User4, User3, User2]
Users with Scores in Range: [User4, User3]

写在最后

有关于Redis中的ZSet数据类型实战应用场景到此就结束了。功能演示代码的逻辑简单,目的是理解ZSet数据类型的应用,实际场景的逻辑根据具体需求而定。感谢大家的阅读,希望大家在评论区对此部分内容散发讨论或者有什么其他场景也可以在评论区提出。


请添加图片描述

你可能感兴趣的:(Redis从头学,redis,数据库,缓存)