Redis 数据结构与命令之有序集合(Sorted Set)解析及应用

Redis 的有序集合(Sorted Set)是一种强大且灵活的数据结构,支持高效的排序操作。本文将详细介绍有序集合的特点、命令及其在排行榜和优先级队列中的典型应用,并结合 Java 示例代码进行实现。


一、有序集合(Sorted Set)简介

1.1 数据结构特点

  1. 自动排序:每个元素与一个分数(score)关联,集合内的元素按分数从小到大排序。
  2. 无重复性:元素值必须唯一,但分数可以重复。
  3. 高效操作
    • 插入、删除、查找操作时间复杂度为 O(log N)。
    • 范围查询、排名查询操作非常高效。

1.2 常用命令

命令 描述 示例
ZADD key score member 向集合添加元素及其分数(若元素已存在则更新分数) ZADD leaderboard 100 "Alice"
ZRANGE key start stop [WITHSCORES] 返回指定排名区间内的元素(可选返回分数) ZRANGE leaderboard 0 2 WITHSCORES
ZREVRANGE key start stop [WITHSCORES] 返回指定排名区间内的元素,按分数从大到小排列 ZREVRANGE leaderboard 0 2 WITHSCORES
ZSCORE key member 获取指定元素的分数 ZSCORE leaderboard "Alice"
ZRANK key member 返回指定元素的排名(从 0 开始,分数从小到大) ZRANK leaderboard "Alice"
ZREVRANK key member 返回指定元素的排名(从 0 开始,分数从大到小) ZREVRANK leaderboard "Alice"
ZREM key member 删除指定元素 ZREM leaderboard "Alice"

二、基于 Sorted Set 实现排行榜

排行榜是一种典型的有序集合应用场景。通过 ZADD 插入玩家分数,利用 ZRANGEZREVRANGE 按照分数顺序获取排名。

2.1 实现原理

  1. 存储分数:使用玩家名称作为元素,分数作为 score 存入 Redis 的 Sorted Set。
  2. 获取排名:根据分数升序或降序,获取玩家排名列表。
  3. 更新分数:直接调用 ZADD,若玩家已存在,分数将被更新。

2.2 Java 示例代码

引入依赖
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>4.3.1</version>
</dependency>
排行榜实现
import redis.clients.jedis.Jedis;
import java.util.Set;

public class RedisLeaderboard {
    private Jedis jedis;
    private String leaderboardKey;

    public RedisLeaderboard(Jedis jedis, String leaderboardKey) {
        this.jedis = jedis;
        this.leaderboardKey = leaderboardKey;
    }

    // 添加玩家分数
    public void addScore(String player, double score) {
        jedis.zadd(leaderboardKey, score, player);
    }

    // 获取排行榜
    public Set<String> getTopPlayers(int topN) {
        return jedis.zrevrange(leaderboardKey, 0, topN - 1);
    }

    // 获取玩家排名
    public long getPlayerRank(String player) {
        Long rank = jedis.zrevrank(leaderboardKey, player);
        return rank == null ? -1 : rank + 1; // 转换为从 1 开始的排名
    }

    // 获取玩家分数
    public double getPlayerScore(String player) {
        Double score = jedis.zscore(leaderboardKey, player);
        return score == null ? 0 : score;
    }

    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        RedisLeaderboard leaderboard = new RedisLeaderboard(jedis, "gameLeaderboard");

        // 添加玩家分数
        leaderboard.addScore("Alice", 1200);
        leaderboard.addScore("Bob", 1500);
        leaderboard.addScore("Charlie", 800);

        // 获取排行榜前 3 名
        Set<String> topPlayers = leaderboard.getTopPlayers(3);
        System.out.println("Top Players: " + topPlayers);

        // 获取玩家排名
        System.out.println("Alice's Rank: " + leaderboard.getPlayerRank("Alice"));

        // 获取玩家分数
        System.out.println("Bob's Score: " + leaderboard.getPlayerScore("Bob"));
    }
}

三、基于 Sorted Set 实现优先级队列

优先级队列是一种按照元素优先级处理任务的数据结构。通过 Redis 的 Sorted Set,我们可以轻松实现一个优先级队列,优先级越高的任务分数越低(分数可以表示延迟时间或任务权重)。

3.1 实现原理

  1. 存储任务:使用任务标识作为元素,优先级或时间戳作为分数。
  2. 获取任务:通过 ZRANGE 获取最高优先级任务。
  3. 删除任务:执行任务后,通过 ZREM 删除任务。

3.2 Java 示例代码

优先级队列实现
import redis.clients.jedis.Jedis;

public class RedisPriorityQueue {
    private Jedis jedis;
    private String queueKey;

    public RedisPriorityQueue(Jedis jedis, String queueKey) {
        this.jedis = jedis;
        this.queueKey = queueKey;
    }

    // 添加任务
    public void addTask(String task, double priority) {
        jedis.zadd(queueKey, priority, task);
    }

    // 获取并移除优先级最高的任务
    public String pollTask() {
        Set<String> tasks = jedis.zrange(queueKey, 0, 0); // 获取分数最小的任务
        if (tasks.isEmpty()) {
            return null;
        }
        String task = tasks.iterator().next();
        jedis.zrem(queueKey, task); // 移除任务
        return task;
    }

    // 获取队列大小
    public long getQueueSize() {
        return jedis.zcard(queueKey);
    }

    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        RedisPriorityQueue queue = new RedisPriorityQueue(jedis, "taskQueue");

        // 添加任务
        queue.addTask("Task1", 1.0); // 优先级 1
        queue.addTask("Task2", 0.5); // 优先级 0.5
        queue.addTask("Task3", 2.0); // 优先级 2

        // 获取并执行任务
        System.out.println("Executing: " + queue.pollTask());
        System.out.println("Executing: " + queue.pollTask());

        // 队列剩余任务数量
        System.out.println("Remaining Tasks: " + queue.getQueueSize());
    }
}

四、总结

Redis 的有序集合(Sorted Set)是一个功能强大的数据结构,在排行榜和优先级队列等场景中有广泛应用。通过本文的介绍,我们实现了以下功能:

  1. 排行榜:基于分数排序获取玩家排名和分数。
  2. 优先级队列:基于优先级动态处理任务。
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐