引言
Redis ZSet(有序集合)是一个独特而强大的数据结构,它结合了Set的去重能力和List的排序能力,同时提供了丰富的集合运算功能。ZSet以其高效的排序和范围查询能力,在排行榜、实时统计等场景中发挥着重要作用。
一、ZSet核心特性
1.1 ZSet的基本概念
ZSet通过给每个元素关联一个score(分数)来实现排序,元素按照score升序排列,score相同的元素按照字典序排列。
ZSet 有序性 唯一性 集合运算 按score升序排列 score相同按字典序 支持范围查询 member不允许重复 score可以重复 支持交集 支持并集 支持差集
1.2 ZSet与Set的对比
| 特性 | ZSet(有序集合) | Set(无序集合) |
|---|---|---|
| 排序方式 | 按score升序排列 | 无序 |
| 元素唯一性 | member唯一 | 元素唯一 |
| 分数支持 | 每个元素有score | 无分数概念 |
| 查询方式 | 按score范围、排名 | 按元素存在性 |
| 底层结构 | 跳表/ziplist | 哈希表/IntSet |
二、ZSet核心命令详解
2.1 基本操作命令
ZADD - 添加/更新元素
redis
# 基本语法
ZADD key [NX|XX] [GT|LT] [CH] [INCR] score member [score member ...]
# 常用示例
# 1. 添加元素
ZADD leaderboard 1000 "Alice" 1500 "Bob" 800 "Charlie"
# 2. 仅当不存在时添加(NX)
ZADD leaderboard NX 1200 "David"
# 3. 仅当存在时更新(XX)
ZADD leaderboard XX 1300 "Alice"
# 4. 仅当分数更大时更新(GT + XX)
ZADD leaderboard XX GT 1400 "Bob"
# 5. 显示新增和修改的元素数(CH)
ZADD leaderboard CH 900 "Charlie"
# 6. 增加分数(INCR)
ZADD leaderboard INCR 50 "Alice"
ZSCORE - 获取分数
redis
# 获取指定元素的分数
ZSCORE key member
# 示例
ZSCORE leaderboard "Alice"
# 返回:"1350"(如果存在)
ZCARD - 获取元素个数
redis
# 获取有序集合中的元素个数
ZCARD key
# 示例
ZCARD leaderboard
# 返回:4
2.2 查询命令
ZRANGE - 按排名范围查询
redis
# 基本语法
ZRANGE key start stop [WITHSCORES]
# 示例
# 1. 获取前3名
ZRANGE leaderboard 0 2
# 返回:"Charlie", "David", "Alice"(按分数升序)
# 2. 获取所有元素(带分数)
ZRANGE leaderboard 0 -1 WITHSCORES
# 返回:"Charlie" "800" "David" "1200" "Alice" "1350" "Bob" "1500"
ZREVRANGE - 逆序范围查询
redis
# 按分数降序查询
ZREVRANGE key start stop [WITHSCORES]
# 示例:获取前三名(分数从高到低)
ZREVRANGE leaderboard 0 2 WITHSCORES
# 返回:"Bob" "1500" "Alice" "1350" "David" "1200"
ZRANGEBYSCORE - 按分数范围查询
redis
# 查询指定分数范围内的元素
ZRANGEBYSCORE key min max [WITHSCORES]
# 示例
# 1. 查询分数在1000-1400之间的元素
ZRANGEBYSCORE leaderboard 1000 1400
# 2. 使用开区间
ZRANGEBYSCORE leaderboard (1000 (1400 # 排除1000和1400
# 3. 使用无穷大
ZRANGEBYSCORE leaderboard -inf +inf # 查询所有元素
ZCOUNT - 统计分数范围内的元素数
redis
# 统计指定分数范围内的元素个数
ZCOUNT key min max
# 示例
ZCOUNT leaderboard 1000 1400
# 返回:3
2.3 排名相关命令
ZRANK / ZREVRANK - 获取排名
redis
# ZRANK:获取正序排名(从0开始,分数最低为第0名)
ZRANK key member
# ZREVRANK:获取逆序排名(从0开始,分数最高为第0名)
ZREVRANK key member
# 示例
ZRANK leaderboard "Alice" # 返回:2(正序第3名)
ZREVRANK leaderboard "Alice" # 返回:1(逆序第2名)
2.4 删除命令
ZREM - 删除指定元素
redis
# 删除一个或多个元素
ZREM key member [member ...]
# 示例
ZREM leaderboard "Charlie"
ZPOPMAX / ZPOPMIN - 弹出最高/最低分元素
redis
# 弹出分数最高的元素
ZPOPMAX key [count]
# 弹出分数最低的元素
ZPOPMIN key [count]
# 示例
ZPOPMAX leaderboard 2 # 弹出分数最高的2个元素
ZPOPMIN leaderboard # 弹出分数最低的1个元素
ZREMRANGEBYRANK - 按排名范围删除
redis
# 删除指定排名范围内的元素
ZREMRANGEBYRANK key start stop
# 示例:删除最后10名
ZREMRANGEBYRANK leaderboard 0 9
ZREMRANGEBYSCORE - 按分数范围删除
redis
# 删除指定分数范围内的元素
ZREMRANGEBYSCORE key min max
# 示例:删除分数低于1000的元素
ZREMRANGEBYSCORE leaderboard -inf 1000
2.5 集合运算命令
ZINTERSTORE - 交集运算并存储
redis
# 计算多个有序集合的交集并存储结果
ZINTERSTORE destination numkeys key [key ...]
[WEIGHTS weight [weight ...]]
[AGGREGATE SUM|MIN|MAX]
# 参数说明:
# WEIGHTS:权重,每个集合的分数乘以此权重
# AGGREGATE:聚合方式,默认SUM(分数相加)
# 示例
ZADD game1 100 "Alice" 200 "Bob"
ZADD game2 150 "Alice" 300 "Charlie"
ZADD game3 120 "Alice" 180 "Bob"
# 计算三个游戏的交集(分数相加)
ZINTERSTORE common_players 3 game1 game2 game3
# 结果:Alice的分数为 100+150+120 = 370
# 计算交集(取最高分数)
ZINTERSTORE common_max 3 game1 game2 game3 AGGREGATE MAX
# 结果:Alice的分数为 max(100,150,120) = 150
# 计算交集(加权计算)
ZINTERSTORE common_weighted 3 game1 game2 game3 WEIGHTS 2 1 0.5
# 结果:Alice的分数为 100*2 + 150*1 + 120*0.5 = 410
ZUNIONSTORE - 并集运算并存储
redis
# 计算多个有序集合的并集并存储结果
ZUNIONSTORE destination numkeys key [key ...]
[WEIGHTS weight [weight ...]]
[AGGREGATE SUM|MIN|MAX]
# 示例
ZUNIONSTORE all_players 3 game1 game2 game3
# 结果:包含Alice, Bob, Charlie,分数按指定方式聚合
三、ZSet底层实现原理
3.1 两种编码方式

3.2 SkipList(跳表)结构
跳表示例(层数:3) L3:头节点 节点F 节点A L2:头节点 节点C 节点F 节点A L1:头节点 节点B 节点C 节点D 节点E 节点F
跳表特点:
- 平均查找复杂度:O(logN)
- 支持范围查询
- 易于实现和维护
- Redis内存占用优化版(每节点随机层数)
3.3 配置优化
redis
# Redis配置文件中的ZSet相关参数
zset-max-ziplist-entries 128 # Ziplist最大元素数量
zset-max-ziplist-value 64 # Ziplist最大元素值(字节)
# 当元素数量超过128或元素值超过64字节时,自动转为SkipList
四、ZSet应用场景详解
实时排行榜系统
游戏分数排行榜
系统清理 排行榜查询 玩家得分更新 ZADD ZADD ZADD ZADD 定期清理
ZREMRANGEBYRANK 100 -1 赛季重置
DEL 赛季key 数据归档
持久化到DB 显示TOP10
ZREVRANGE 0 9 玩家排名查询
ZREVRANK 玩家名 分数段统计
ZCOUNT min max 附近玩家
ZRANGEBYSCORE 排行榜 ZSet 玩家A得分:1500 玩家B得分:1800 玩家C得分:1200 玩家D得分:2000 前端展示 个人中心 数据分析 社交功能
实现代码:
redis
# 1. 玩家得分更新
ZADD game:leaderboard:season1 1500 "player:A"
ZADD game:leaderboard:season1 1800 "player:B"
ZADD game:leaderboard:season1 INCR 50 "player:A" # 增加50分
# 2. 获取排行榜(前10名,带分数)
ZREVRANGE game:leaderboard:season1 0 9 WITHSCORES
# 3. 获取玩家排名
ZREVRANK game:leaderboard:season1 "player:A"
# 4. 获取玩家分数
ZSCORE game:leaderboard:season1 "player:A"
# 5. 获取分数段玩家数(1000-2000分)
ZCOUNT game:leaderboard:season1 1000 2000
# 6. 获取附近玩家(当前玩家前后5名)
current_rank = ZREVRANK game:leaderboard:season1 "player:A"
ZREVRANGE game:leaderboard:season1 (current_rank-5) (current_rank+5)
# 7. 赛季结束时清理数据
# 7.1 持久化到数据库
# 7.2 清空Redis数据
DEL game:leaderboard:season1
微博热搜榜
ZSet ZSet ZSet ZSet 用户行为 多维度数据 阅读量 点赞量 评论量 转发量 hot:read hot:like hot:comment hot:share ZUNIONSTORE
加权合并 hot:total
综合热搜榜 前端展示 热搜1: 话题A 热搜2: 话题B 热搜3: 话题C
实现代码
redis
# 热搜榜实现:基于多种因素加权计算
# 1. 创建各维度ZSet
ZADD hot:read 1000 "话题A" 800 "话题B" 500 "话题C"
ZADD hot:like 300 "话题A" 400 "话题B" 200 "话题C"
ZADD hot:comment 150 "话题A" 100 "话题B" 80 "话题C"
ZADD hot:share 50 "话题A" 30 "话题B" 20 "话题C"
# 2. 计算综合热度(加权聚合)
# 权重:阅读1.0,点赞2.0,评论3.0,分享4.0
ZUNIONSTORE hot:total 4 hot:read hot:like hot:comment hot:share
WEIGHTS 1.0 2.0 3.0 4.0
# 3. 获取实时热搜TOP10
ZREVRANGE hot:total 0 9 WITHSCORES
# 4. 定时更新(每分钟)
# 通过脚本定时更新各维度数据并重新计算
五、总结
ZSet核心优势:
- 高效排序:O(logN)的插入、删除和查询复杂度
- 灵活查询:支持按排名、分数范围、元素查询
- 丰富运算:支持交、并集运算,支持权重和聚合方式
- 内存优化:智能选择Ziplist或Skiplist编码
适用场景总结:
- 排行榜系统:游戏、社交、电商等各种排名场景
- 延迟任务:订单超时、定时提醒、任务调度
- 实时统计:在线用户、文章热度、系统监控
- 优先级队列:任务调度、消息队列
- 地理位置:附近搜索、范围查询
- 时间序列:监控数据、日志记录