Redis ZSet 教程:有序集合入门指南 – wiki大全

Redis ZSet 教程:有序集合入门指南

Redis 有序集合(Sorted Sets),简称 ZSet,是 Redis 中一种独特且功能强大的数据结构。它结合了传统集合(Set)的无重复性以及列表(List)的有序性,使其在多种应用场景中表现出色。本教程将深入探讨 Redis ZSet 的概念、核心命令及其常见应用。

什么是 Redis 有序集合 (ZSet)?

Redis ZSet 是一个由唯一字符串成员组成的集合,每个成员都关联了一个浮点数分数(score)。ZSet 的核心特性在于:

  • 唯一成员 (Unique Members):与普通 Redis Set 类似,ZSet 中的每个成员都是唯一的。您不能添加重复的成员;如果尝试添加已存在的成员,其分数将被更新。
  • 按分数排序 (Ordered by Score):ZSet 中的成员始终根据其关联的分数进行排序。默认情况下,成员从分数最小到分数最大进行升序排列。如果分数相同,则按成员的字典顺序排序。
  • 高效检索 (Efficient Retrieval):ZSet 允许您高效地根据分数范围或成员的排名(rank,即在排序列表中的位置)来检索元素。
  • 内部实现 (Internal Implementation):为了优化内存使用和性能,Redis 会根据 ZSet 的大小和成员的长度自动选择不同的内部数据结构。
    • 对于小型的 ZSet(通常少于 128 个元素,且每个成员的字符串长度小于 64 字节),Redis 使用高度内存效率的 ziplist
    • 当 ZSet 变得更大时,Redis 会自动切换到 skiplist(跳跃表),这是一种在 O(log N) 时间复杂度内执行查找、插入和删除操作的结构,特别适合范围查询。

ZSet 是实现排行榜、优先级队列、时间序列索引等功能理想的数据结构。

基本 ZSet 命令

以下是与 Redis ZSet 交互的一些基本命令:

  1. ZADD key score member [score member ...]
    将一个或多个成员及其分数添加到有序集合中。如果成员已存在,则更新其分数。

    redis
    ZADD leaderboard 100 "Alice" 200 "Bob" 150 "Carol"
    (integer) 3

  2. ZRANGE key start stop [WITHSCORES]
    返回有序集合中指定范围内的成员,按分数从低到高排序。startstop 是 0-based 索引。0 -1 表示返回所有成员。WITHSCORES 选项会同时返回分数。

    “`redis
    ZRANGE leaderboard 0 -1
    1) “Alice”
    2) “Carol”
    3) “Bob”

    ZRANGE leaderboard 0 -1 WITHSCORES
    1) “Alice”
    2) “100”
    3) “Carol”
    4) “150”
    5) “Bob”
    6) “200”
    “`

  3. ZREVRANGE key start stop [WITHSCORES]
    ZRANGE 类似,但返回成员的顺序是按分数从高到低排序(逆序)。

    redis
    ZREVRANGE leaderboard 0 -1 WITHSCORES
    1) "Bob"
    2) "200"
    3) "Carol"
    4) "150"
    5) "Alice"
    6) "100"

  4. ZREM key member [member ...]
    从有序集合中移除一个或多个指定的成员。

    “`redis
    ZREM leaderboard “Alice”
    (integer) 1

    ZRANGE leaderboard 0 -1
    1) “Carol”
    2) “Bob”
    “`

  5. ZCARD key
    返回有序集合中成员的数量(基数)。

    redis
    ZCARD leaderboard
    (integer) 2

  6. ZSCORE key member
    返回有序集合中指定成员的分数。

    redis
    ZSCORE leaderboard "Bob"
    "200"

高级 ZSet 命令

这些命令提供了更复杂的查询和操作有序集合的方式:

  1. ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
    返回有序集合中分数在 minmax 范围内的所有成员(包含边界)。您可以使用 ( 前缀表示开区间(例如 (100 表示分数大于 100)。LIMIT 选项可用于分页。

    redis
    ZADD leaderboard 100 "Alice" 200 "Bob" 150 "Carol" 250 "David"
    ZRANGEBYSCORE leaderboard 150 200 WITHSCORES
    1) "Carol"
    2) "150"
    3) "Bob"
    4) "200"

  2. ZCOUNT key min max
    统计有序集合中分数在 minmax 范围内的成员数量。

    redis
    ZCOUNT leaderboard 150 200
    (integer) 2

  3. ZRANK key member
    返回成员在有序集合中的 0-based 排名,按分数从低到高排序。

    redis
    ZRANK leaderboard "Carol"
    (integer) 1

  4. ZREVRANK key member
    返回成员在有序集合中的 0-based 排名,按分数从高到低排序。

    redis
    ZREVRANK leaderboard "Carol"
    (integer) 2

  5. ZINCRBY key increment member
    将有序集合中指定成员的分数增加 increment。如果成员不存在,则将其添加并以 increment 作为其初始分数。

    redis
    ZINCRBY leaderboard 50 "Alice"
    "150"
    ZSCORE leaderboard "Alice"
    "150"

  6. ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
    计算多个有序集合的并集,并将结果存储在 destination 新建的有序集合中。可以通过 WEIGHTS 为每个集合的成员分数指定权重,并通过 AGGREGATE 参数指定如何聚合同名成员的分数(SUMMINMAX)。

    redis
    ZADD set1 1 "one" 2 "two"
    ZADD set2 1 "one" 3 "three"
    ZUNIONSTORE union_set 2 set1 set2 AGGREGATE SUM
    (integer) 3
    ZRANGE union_set 0 -1 WITHSCORES
    1) "one"
    2) "2" (1 from set1 + 1 from set2)
    3) "two"
    4) "2"
    5) "three"
    6) "3"

  7. ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
    ZUNIONSTORE 类似,但计算的是多个有序集合的交集。

    redis
    ZINTERSTORE intersection_set 2 set1 set2 AGGREGATE MIN
    (integer) 1
    ZRANGE intersection_set 0 -1 WITHSCORES
    1) "one"
    2) "1"

  8. ZREMRANGEBYRANK key start stop
    移除有序集合中指定排名范围内的所有成员。

    redis
    ZREMRANGEBYRANK leaderboard 0 0
    (integer) 1
    ZRANGE leaderboard 0 -1
    1) "Carol"
    2) "Bob"
    3) "David"

  9. ZREMRANGEBYSCORE key min max
    移除有序集合中指定分数范围内的所有成员。

    redis
    ZREMRANGEBYSCORE leaderboard 200 250
    (integer) 2
    ZRANGE leaderboard 0 -1
    1) "Carol"
    2) "150"

Redis ZSet 的常见应用场景

Redis ZSet 的多功能性使其适用于多种实际应用:

  • 排行榜 (Leaderboards):最经典的用例。在游戏、健身应用或任何需要显示用户排名的地方,ZSet 可以轻松维护和查询最高分数。
  • 限流器 (Rate Limiting):可以使用成员存储请求的用户 ID,分数存储请求时间戳。结合 ZREMRANGEBYSCORE 移除旧请求和 ZCARD 统计近期请求数量,可以有效实现限流。
  • 时间序列数据 (Time-Series Data):将事件的时间戳作为分数,事件数据作为成员,可以高效地按时间范围查询数据。
  • 优先级队列 (Priority Queues):将任务作为成员,任务的优先级或计划执行时间作为分数,可以实现一个简单的优先级队列。
  • 热门内容推荐 (Trending Feeds):根据文章、视频或商品的互动分数(如浏览量、点赞数)进行排序,实时展示热门内容。

Python 代码示例

在 Python 中,您可以使用 redis-py 客户端库来操作 Redis ZSet。

首先,确保安装了 redis-py
bash
pip install redis

以下是一个简单的 Python 脚本,演示了部分 ZSet 命令的使用:

“`python
import redis

连接到 Redis

假设 Redis 运行在 localhost:6379

r = redis.Redis(host=’localhost’, port=6379, db=0, decode_responses=True)

— 基本 ZSet 操作 —

1. ZADD: 向有序集合添加成员 (例如,游戏排行榜)

print(“— ZADD —“)
r.delete(“game_leaderboard”) # 清除之前的数据以进行演示
r.zadd(“game_leaderboard”, {“playerA”: 1500, “playerB”: 2000, “playerC”: 1200, “playerD”: 1800})
print(“已将玩家添加到排行榜。”)

2. ZRANGE: 获取所有成员及其分数,按分数升序排列

print(“\n— ZRANGE (所有,升序) —“)

ZRANGE 在 withscores=True 时返回 (成员, 分数) 元组列表

leaderboard_asc = r.zrange(“game_leaderboard”, 0, -1, withscores=True)
for player, score in leaderboard_asc:
print(f”{player}: {int(score)}”)

3. ZREVRANGE: 获取排名前2的成员及其分数,按分数降序排列

print(“\n— ZREVRANGE (前2名,降序) —“)
leaderboard_desc = r.zrevrange(“game_leaderboard”, 0, 1, withscores=True)
for player, score in leaderboard_desc:
print(f”{player}: {int(score)}”)

4. ZSCORE: 获取特定玩家的分数

print(“\n— ZSCORE —“)
player_b_score = r.zscore(“game_leaderboard”, “playerB”)
print(f”玩家B的分数: {int(player_b_score)}”)

5. ZINCRBY: 增加玩家的分数

print(“\n— ZINCRBY —“)
r.zincrby(“game_leaderboard”, 500, “playerA”)
player_a_new_score = r.zscore(“game_leaderboard”, “playerA”)
print(f”玩家A增加分数后的新分数: {int(player_a_new_score)}”)

6. ZRANK: 获取玩家的排名 (0-based, 升序分数)

print(“\n— ZRANK —“)
player_a_rank = r.zrank(“game_leaderboard”, “playerA”)
print(f”玩家A的排名 (升序): {player_a_rank}”)

7. ZREVRANK: 获取玩家的逆向排名 (0-based, 降序分数)

print(“\n— ZREVRANK —“)
player_a_revrank = r.zrevrank(“game_leaderboard”, “playerA”)
print(f”玩家A的逆向排名 (降序): {player_a_revrank}”)

8. ZCARD: 获取玩家总数

print(“\n— ZCARD —“)
num_players = r.zcard(“game_leaderboard”)
print(f”排行榜中的玩家总数: {num_players}”)

9. ZRANGEBYSCORE: 获取分数在 1500 到 2000 之间的玩家

print(“\n— ZRANGEBYSCORE (1500 到 2000) —“)
players_in_range = r.zrangebyscore(“game_leaderboard”, 1500, 2000, withscores=True)
for player, score in players_in_range:
print(f”{player}: {int(score)}”)

10. ZREM: 移除一个玩家

print(“\n— ZREM —“)
r.zrem(“game_leaderboard”, “playerC”)
print(“已移除玩家C。”)

验证移除后的排行榜

print(“\n— 移除后的排行榜 —“)
final_leaderboard = r.zrange(“game_leaderboard”, 0, -1, withscores=True)
for player, score in final_leaderboard:
print(f”{player}: {int(score)}”)

清理

r.delete(“game_leaderboard”)
r.delete(“set1”)
r.delete(“set2”)
r.delete(“union_set”)
r.delete(“intersection_set”)
print(“\n已清理 Redis 键。”)
“`

结论

Redis 有序集合 (ZSet) 是 Redis 提供的一种强大且灵活的数据结构,通过将唯一成员与可排序的分数相结合,极大地简化了需要排序和范围查询的复杂场景。无论是构建游戏排行榜、实现实时推荐系统,还是处理时间序列数据,ZSet 都能提供高效且可靠的解决方案。掌握 ZSet 的使用,将使您能够更有效地利用 Redis 的强大功能。

滚动至顶部