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(通常少于 128 个元素,且每个成员的字符串长度小于 64 字节),Redis 使用高度内存效率的
ZSet 是实现排行榜、优先级队列、时间序列索引等功能理想的数据结构。
基本 ZSet 命令
以下是与 Redis ZSet 交互的一些基本命令:
-
ZADD key score member [score member ...]
将一个或多个成员及其分数添加到有序集合中。如果成员已存在,则更新其分数。redis
ZADD leaderboard 100 "Alice" 200 "Bob" 150 "Carol"
(integer) 3 -
ZRANGE key start stop [WITHSCORES]
返回有序集合中指定范围内的成员,按分数从低到高排序。start和stop是 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”
“` -
ZREVRANGE key start stop [WITHSCORES]
与ZRANGE类似,但返回成员的顺序是按分数从高到低排序(逆序)。redis
ZREVRANGE leaderboard 0 -1 WITHSCORES
1) "Bob"
2) "200"
3) "Carol"
4) "150"
5) "Alice"
6) "100" -
ZREM key member [member ...]
从有序集合中移除一个或多个指定的成员。“`redis
ZREM leaderboard “Alice”
(integer) 1ZRANGE leaderboard 0 -1
1) “Carol”
2) “Bob”
“` -
ZCARD key
返回有序集合中成员的数量(基数)。redis
ZCARD leaderboard
(integer) 2 -
ZSCORE key member
返回有序集合中指定成员的分数。redis
ZSCORE leaderboard "Bob"
"200"
高级 ZSet 命令
这些命令提供了更复杂的查询和操作有序集合的方式:
-
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
返回有序集合中分数在min和max范围内的所有成员(包含边界)。您可以使用(前缀表示开区间(例如(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" -
ZCOUNT key min max
统计有序集合中分数在min和max范围内的成员数量。redis
ZCOUNT leaderboard 150 200
(integer) 2 -
ZRANK key member
返回成员在有序集合中的 0-based 排名,按分数从低到高排序。redis
ZRANK leaderboard "Carol"
(integer) 1 -
ZREVRANK key member
返回成员在有序集合中的 0-based 排名,按分数从高到低排序。redis
ZREVRANK leaderboard "Carol"
(integer) 2 -
ZINCRBY key increment member
将有序集合中指定成员的分数增加increment。如果成员不存在,则将其添加并以increment作为其初始分数。redis
ZINCRBY leaderboard 50 "Alice"
"150"
ZSCORE leaderboard "Alice"
"150" -
ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
计算多个有序集合的并集,并将结果存储在destination新建的有序集合中。可以通过WEIGHTS为每个集合的成员分数指定权重,并通过AGGREGATE参数指定如何聚合同名成员的分数(SUM、MIN、MAX)。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" -
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" -
ZREMRANGEBYRANK key start stop
移除有序集合中指定排名范围内的所有成员。redis
ZREMRANGEBYRANK leaderboard 0 0
(integer) 1
ZRANGE leaderboard 0 -1
1) "Carol"
2) "Bob"
3) "David" -
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 的强大功能。