Redis

关注公众号 jb51net

关闭
首页 > 数据库 > Redis > Redis SortedSet类型

Redis中SortedSet类型的实现示例

作者:咖啡の猫

本文主要介绍了Redis中SortedSet类型的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

一、前言:ZSet 不是“排序列表”,而是有序集合引擎

Redis 的 Sorted Set(有序集合,简称 ZSet) 是 Redis 最强大的数据类型之一。
它结合了 Set 的唯一性 和 Score 的排序能力,每个成员(member)关联一个双精度浮点数分数(score),并按 score 自动排序。

典型应用场景包括:

本文将系统讲解 ZSet 的核心命令,并通过真实业务案例展示其强大能力。

二、ZSet 核心命令速查表

命令作用时间复杂度
ZADD key [NX|XX] [CH] [INCR] score member [score member ...]添加或更新成员O(log N) 每个成员
ZREM key member [member ...]移除成员O(log N) 每个成员
ZSCORE key member获取成员的分数O(1)
ZRANK key member获取成员正序排名(从 0 开始)O(log N)
ZREVRANK key member获取成员倒序排名O(log N)

范围查询命令

命令作用时间复杂度
ZRANGE key start stop [WITHSCORES]按正序获取范围成员O(log N + M)
ZREVRANGE key start stop [WITHSCORES]按倒序获取范围成员O(log N + M)
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]按分数范围查询O(log N + M)
ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]按分数范围倒序查询O(log N + M)

集合操作 & 统计

命令作用时间复杂度
ZCARD key获取集合大小O(1)
ZCOUNT key min max统计分数在 [min, max] 的成员数O(log N)
ZINCRBY key increment member对成员分数加减O(log N)
ZPOPMIN key [count]弹出分数最小的成员O(log N × count)
ZPOPMAX key [count]弹出分数最大的成员O(log N × count)
BZPOPMIN key [key ...] timeout阻塞式弹出最小成员O(log N)
BZPOPMAX key [key ...] timeout阻塞式弹出最大成员O(log N)

💡 关键特性:

  • 成员唯一(重复添加会更新 score)
  • 按 score 自动排序(支持相同 score,此时按字典序排)
  • 支持高效范围查询与弹出

三、常用命令详解与示例

3.1 添加与更新:ZADD

# 添加玩家积分(score=积分,member=用户ID)
127.0.0.1:6379> ZADD game:rank 1500 "user:1001" 1800 "user:1002" 1200 "user:1003"
(integer) 3

# 更新用户积分(覆盖旧值)
127.0.0.1:6379> ZADD game:rank 1600 "user:1001"
(integer) 0  # 成员已存在,仅更新

# 仅当成员不存在时添加(NX)
127.0.0.1:6379> ZADD game:rank NX 2000 "user:1004"

3.2 排行榜查询:ZREVRANGE(倒序 = 从高到低)

# 获取 TOP 3(带分数)
127.0.0.1:6379> ZREVRANGE game:rank 0 2 WITHSCORES
1) "user:1002"
2) "1800"
3) "user:1001"
4) "1600"
5) "user:1003"
6) "1200"

# 获取用户排名(从 0 开始)
127.0.0.1:6379> ZREVRANK game:rank "user:1001"
(integer) 1  # 第 2 名(0-indexed)

✅ 这是实现“游戏排行榜”、“热销榜”的标准做法!

3.3 分数范围查询:ZRANGEBYSCORE

# 查询积分在 1500~2000 的用户
127.0.0.1:6379> ZRANGEBYSCORE game:rank 1500 2000 WITHSCORES
1) "user:1001"
2) "1600"
3) "user:1002"
4) "1800"

# 分页查询(每页 10 条)
127.0.0.1:6379> ZREVRANGEBYSCORE game:rank +inf -inf LIMIT 10 10

🔍 注意:

  • +inf 表示正无穷,-inf 表示负无穷
  • LIMIT offset count 支持分页,但大数据量下 offset 大时性能下降

3.4 延迟队列:ZADD + BZPOPMIN

# 添加延迟任务(score = 执行时间戳)
127.0.0.1:6379> ZADD delay_queue 1700000000 "order:cancel:1001"
127.0.0.1:6379> ZADD delay_queue 1700003600 "email:send:2001"

# 消费者:阻塞等待可执行任务
127.0.0.1:6379> BZPOPMIN delay_queue 5
1) "delay_queue"
2) "order:cancel:1001"
3) "1700000000"

✅ 优势:天然支持定时、去重、可靠消费
❌ 局限:不支持 ACK(需配合 Lua 或状态标记)

四、ZSet 的内部编码

Redis 对小 ZSet 使用 ziplist 编码,大 ZSet 使用 skiplist(跳跃表) + dict(哈希表):

# redis.conf 默认配置
zset-max-ziplist-entries 128
zset-max-ziplist-value 64

💡 建议:

  • 排行榜、延迟队列等场景性能极佳
  • 避免单个 ZSet 超过 100 万元素

五、实战应用场景

场景 1:实时游戏排行榜

// Java (Lettuce)
String rankKey = "game:season_2025:rank";

// 玩家得分更新
redis.zadd(rankKey, score, "player:" + playerId);

// 获取 TOP 50
List<ScoredValue<String>> top50 = redis.zrevrangeWithScores(rankKey, 0, 49);

// 获取玩家排名
Long rank = redis.zrevrank(rankKey, "player:" + playerId);

📌 优化:对超大排行榜,可分桶(如每 1 万一名一个 ZSet)

场景 2:订单超时取消(延迟队列)

import time
import redis

r = redis.Redis()

# 创建订单时加入延迟队列(30分钟后取消)
expire_time = int(time.time()) + 1800
r.zadd("order:delay_cancel", {f"order:{order_id}": expire_time})

# 后台消费者
while True:
    now = int(time.time())
    # 弹出所有已到期订单
    expired = r.zrangebyscore("order:delay_cancel", 0, now)
    if expired:
        for order in expired:
            cancel_order(order)
            r.zrem("order:delay_cancel", order)
    time.sleep(1)

✅ 更优方案:使用 BZPOPMIN 实现阻塞消费,避免轮询

场景 3:带权重的任务调度

# 任务权重:高优先级任务 score 更小(先执行)
ZADD task_queue 10 "urgent_task_1"
ZADD task_queue 50 "normal_task_1"
ZADD task_queue 100 "low_task_1"

# 消费者取最高优先级任务
BZPOPMIN task_queue 0

六、常见误区与最佳实践

❌ 误区 1:用ZRANGE 0 -1获取全量数据

❌ 误区 2:在 ZSet 中存储大 Value

✅ 最佳实践

  1. score 设计合理:
    • 排行榜:直接用分数
    • 延迟队列:用 Unix 时间戳(秒或毫秒)
  2. 设置 TTL:临时 ZSet(如活动排行榜)加过期时间
  3. 监控大小:通过 ZCARD + 告警防止膨胀
  4. 避免大 offset 分页:改用游标或分桶

七、ZSet vs 其他类型选型建议

需求推荐类型
实时排行榜ZSet
延迟/定时任务ZSet(score=时间戳)
唯一性集合Set
顺序敏感队列List
对象属性Hash

📌 ZSet 的核心价值:排序 + 唯一 + 范围查询

八、结语

到此这篇关于Redis中SortedSet类型的实现示例的文章就介绍到这了,更多相关Redis SortedSet类型内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文