Redis 有序集合(上)
有序集合是带分数的集合,元素按分数排序。本课学习有序集合的基本操作。
什么是有序集合?
有序集合的特点:
- 有序:元素按分数从小到大排序
- 不重复:元素不重复,但分数可以重复
- 每个元素关联一个分数:用于排序
leaderboard
├─ "player1" → score: 100
├─ "player2" → score: 200
├─ "player3" → score: 300
└─ "player4" → score: 400
💡 底层实现: 有序集合使用跳表(skiplist)+ 哈希表。跳表支持高效的排序和范围查询,哈希表支持快速查找分数。
ZADD:添加元素
基本用法
REDIS
# 添加单个元素
ZADD leaderboard 100 "player1"
(integer) 1 # 返回新增的元素数量
# 添加多个元素
ZADD leaderboard 200 "player2" 300 "player3" 400 "player4"
(integer) 3
ZADD参数
REDIS
# NX:仅添加新元素,不更新已存在的元素
ZADD leaderboard NX 150 "player1"
(integer) 0 # player1已存在,未更新
# XX:仅更新已存在的元素,不添加新元素
ZADD leaderboard XX 500 "player5"
(integer) 0 # player5不存在,未添加
# CH:返回发生变化的元素数量(包括更新)
ZADD leaderboard CH 120 "player1"
(integer) 1 # 更新了player1的分数
# INCR:增加分数(相当于ZINCRBY)
ZADD leaderboard INCR 50 "player1"
"170" # 返回新分数
更新已存在元素的分数
REDIS
# 如果元素已存在,更新分数
ZADD leaderboard 250 "player1"
# 查看新分数
ZSCORE leaderboard "player1"
"250"
ZSCORE:获取元素的分数
REDIS
ZADD leaderboard 100 "player1" 200 "player2"
# 获取分数
ZSCORE leaderboard "player1"
"100"
# 元素不存在
ZSCORE leaderboard "notexist"
(nil)
ZRANK和ZREVRANK:获取排名
ZRANK:从低到高的排名
REDIS
ZADD leaderboard 100 "player1" 200 "player2" 300 "player3"
# 排名从0开始
ZRANK leaderboard "player1"
(integer) 0 # 第1名(分数最低)
ZRANK leaderboard "player2"
(integer) 1 # 第2名
ZRANK leaderboard "player3"
(integer) 2 # 第3名
ZREVRANK:从高到低的排名
REDIS
# 从高到低的排名(分数最高的排第0)
ZREVRANK leaderboard "player3"
(integer) 0 # 第1名(分数最高)
ZREVRANK leaderboard "player2"
(integer) 1 # 第2名
ZREVRANK leaderboard "player1"
(integer) 2 # 第3名
💡 排名规则:
- ZRANK:分数从低到高,分数最低的排名为0
- ZREVRANK:分数从高到低,分数最高的排名为0
- 排名从0开始,第1名的排名是0
ZRANGE和ZREVRANGE:获取范围内的元素
ZRANGE:按排名范围获取(从低到高)
REDIS
ZADD leaderboard 100 "player1" 200 "player2" 300 "player3" 400 "player4"
# 获取所有元素
ZRANGE leaderboard 0 -1
1) "player1"
2) "player2"
3) "player3"
4) "player4"
# 获取前2个元素
ZRANGE leaderboard 0 1
1) "player1"
2) "player2"
# 获取最后2个元素
ZRANGE leaderboard -2 -1
1) "player3"
2) "player4"
# 获取元素和分数
ZRANGE leaderboard 0 -1 WITHSCORES
1) "player1"
2) "100"
3) "player2"
4) "200"
5) "player3"
6) "300"
7) "player4"
8) "400"
ZREVRANGE:按排名范围获取(从高到低)
REDIS
# 从高到低获取所有元素
ZREVRANGE leaderboard 0 -1 WITHSCORES
1) "player4"
2) "400"
3) "player3"
4) "300"
5) "player2"
6) "200"
7) "player1"
8) "100"
# 获取前3名(分数最高的3个)
ZREVRANGE leaderboard 0 2 WITHSCORES
1) "player4"
2) "400"
3) "player3"
4) "300"
5) "player2"
6) "200"
ZCARD:获取元素数量
REDIS
ZADD leaderboard 100 "player1" 200 "player2" 300 "player3"
ZCARD leaderboard
(integer) 3
ZCOUNT:统计分数范围内的元素数量
REDIS
ZADD leaderboard 100 "player1" 150 "player2" 200 "player3" 250 "player4" 300 "player5"
# 统计分数在150到250之间的元素数量
ZCOUNT leaderboard 150 250
(integer) 3 # player2, player3, player4
# 使用-inf和+inf表示负无穷和正无穷
ZCOUNT leaderboard -inf +inf
(integer) 5 # 所有元素
ZREM:删除元素
REDIS
ZADD leaderboard 100 "player1" 200 "player2" 300 "player3"
# 删除单个元素
ZREM leaderboard "player1"
(integer) 1
# 删除多个元素
ZREM leaderboard "player2" "player3"
(integer) 2
# 删除不存在的元素
ZREM leaderboard "notexist"
(integer) 0
ZINCRBY:增加分数
REDIS
ZADD leaderboard 100 "player1"
# 增加50分
ZINCRBY leaderboard 50 "player1"
"150" # 返回新分数
# 减少分数(传负数)
ZINCRBY leaderboard -20 "player1"
"130"
有序集合应用场景
场景1:游戏排行榜
REDIS
# 更新玩家分数
ZADD game:leaderboard 1500 "player:123"
ZADD game:leaderboard 2300 "player:456"
ZADD game:leaderboard 1800 "player:789"
# 获取前10名
ZREVRANGE game:leaderboard 0 9 WITHSCORES
# 获取玩家排名
ZREVRANK game:leaderboard "player:123"
# 增加玩家分数
ZINCRBY game:leaderboard 100 "player:123"
场景2:热搜榜
REDIS
# 更新热搜热度
ZADD hot:search 10000 "Redis教程"
ZADD hot:search 8000 "Python入门"
ZADD hot:search 15000 "JavaScript"
# 获取前10热搜
ZREVRANGE hot:search 0 9 WITHSCORES
# 增加热度
ZINCRBY hot:search 100 "Redis教程"
场景3:延迟队列
REDIS
# 添加延迟任务(分数为执行时间戳)
ZADD delay:queue 1719129600 "task:1" # 2024-06-23 10:00:00
ZADD delay:queue 1719133200 "task:2" # 2024-06-23 11:00:00
# 获取当前时间之前的任务(需要执行的任务)
ZRANGEBYSCORE delay:queue -inf 1719129600
场景4:带权重的标签
REDIS
# 用户兴趣标签(分数为兴趣度)
ZADD user:1:interests 100 "redis"
ZADD user:1:interests 80 "database"
ZADD user:1:interests 60 "cache"
# 获取用户最感兴趣的标签
ZREVRANGE user:1:interests 0 4 WITHSCORES
场景5:成绩排名
REDIS
# 学生成绩
ZADD exam:math 95 "student:1"
ZADD exam:math 88 "student:2"
ZADD exam:math 92 "student:3"
# 获取成绩排名
ZREVRANGE exam:math 0 -1 WITHSCORES
# 获取学生排名
ZREVRANK exam:math "student:1"
有序集合编码优化
Redis会根据有序集合大小自动选择编码:
| 编码类型 | 使用条件 | 说明 |
|---|---|---|
| ziplist | 元素数量 ≤ 128,元素长度 ≤ 64字节 | 压缩列表,节省内存 |
| skiplist | 元素数量 > 128 或元素长度 > 64字节 | 跳表 + 哈希表,性能更好 |
查看编码类型
REDIS
ZADD small:zset 1 "a" 2 "b" 3 "c"
OBJECT ENCODING small:zset
"ziplist"
# 添加大量元素后
OBJECT ENCODING large:zset
"skiplist"
❓ 常见问题
Q 有序集合和集合的区别?
A 有序集合每个元素关联分数并按分数排序,集合无序。有序集合适合排行榜、排序场景。
Q 分数可以是小数吗?
A 可以。分数是双精度浮点数,支持小数。
Q ZRANK返回的排名从0还是1开始?
A 从0开始。第1名的排名是0,第2名的排名是1。
Q 如何获取分数最高的元素?
A 使用ZREVRANGE key 0 0,或使用ZRANGE key -1 -1。
Q 元素可以重复吗?
A 不可以。元素唯一,但分数可以重复。
📖 小节
- 有序集合是带分数的集合,元素按分数排序
- ZADD添加元素,ZSCORE获取分数
- ZRANK/ZREVRANK获取排名(从低到高/从高到低)
- ZRANGE/ZREVRANGE按排名范围获取元素
- ZCARD获取元素数量,ZCOUNT统计分数范围内的元素
- ZREM删除元素,ZINCRBY增加分数
- 有序集合应用:排行榜、热搜、延迟队列、成绩排名
📝 作业
- 排行榜: 使用有序集合实现游戏排行榜,更新分数、查看排名、获取前10名
- 热搜榜: 实现热搜榜,更新热度、查看热搜、增加热度
- 成绩排名: 使用有序集合存储学生成绩,查看排名和成绩
- 延迟队列: 使用有序集合实现延迟队列,添加任务、获取到期任务
下一课
下一课我们将学习 Redis 有序集合(下),学习有序集合的高级操作。



