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 不可以。元素唯一,但分数可以重复。

📖 小节

📝 作业

  1. 排行榜: 使用有序集合实现游戏排行榜,更新分数、查看排名、获取前10名
  2. 热搜榜: 实现热搜榜,更新热度、查看热搜、增加热度
  3. 成绩排名: 使用有序集合存储学生成绩,查看排名和成绩
  4. 延迟队列: 使用有序集合实现延迟队列,添加任务、获取到期任务

下一课

下一课我们将学习 Redis 有序集合(下),学习有序集合的高级操作。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏