Redis有序集合
有序集合(ZSet/Sorted Set)是带分数(score)排序的集合,元素不重复,按分数升序排列,适合排行榜、评分系统等场景。
结构概述
基本特点
Bash
- 不重复字符串集合
- 每个元素关联一个分数(score)
- 按分数升序排列
- 支持范围查询和排名查询
- 单个ZSet最多2^32-1个元素
结构示意
Bash
score(分数) → member(元素)
100 → player1
95 → player2
90 → player3
85 → player4
80 → player5
按分数升序排列,分数可重复,元素不重复
内部编码
ziplist编码
Bash
触发条件:
- 元素数量 ≤ zset-max-ziplist-entries(默认128)
- 所有元素大小 ≤ zset-max-ziplist-value(默认64字节)
特点:
- 紧凑连续内存
- 元素和分数顺序存储
- 内存效率高
- 查找效率较低
skiplist编码
Bash
触发条件:
- 元素数量 > 128
- 或元素大小 > 64字节
特点:
- 跳表 + 哈希表组合
- 跳表:有序结构,支持范围查询
- 哈希表:O(1)查找元素分数
- 查找和范围查询高效
查看编码
Bash
ZADD leaderboard 100 "player1"
OBJECT ENCODING leaderboard
# 返回: "ziplist"
for i in {1..200}; do
ZADD large:zset $i "player$i"
done
OBJECT ENCODING large:zset
# 返回: "skiplist"
跳表原理
跳表结构
Bash
多层链表结构,概率性分层:
Level 4: [100]────────────────────→
Level 3: [100]───────[200]────────→
Level 2: [50][100][150][200][250][300]→
Level 1: [50][100][150][200][250][300]→
从高层快速跳跃,减少比较次数
查找效率O(logN),接近平衡树
跳表优势
Bash
1. 查找高效:O(logN)
2. 插入高效:O(logN)
3. 删除高效:O(logN)
4. 范围查询:O(logN + M)
5. 实现简单:比平衡树简单
6. 内存占用:比平衡树小
Redis跳表实现
Bash
- 最大32层
- 随机决定层数
- 每层概率为25%
- 同时维护哈希表存储分数
应用场景
1. 排行榜
Bash
# 游戏排行榜
ZADD leaderboard 1000 "player1" 950 "player2" 900 "player3"
# 获取前10名(降序)
ZREVRANGE leaderboard 0 9 WITHSCORES
# 获取玩家排名
ZREVRANK leaderboard "player1"
# 增加分数
ZINCRBY leaderboard 50 "player1"
2. 热榜/热搜
Bash
# 热门文章(浏览量为分数)
ZADD hot:articles 1000 "article:500" 800 "article:501"
# 获取热门文章
ZREVRANGE hot:articles 0 10
# 增加热度
ZINCRBY hot:articles 100 "article:500"
3. 延时队列
Bash
# 任务执行时间为分数
ZADD delay:queue 1700000000 "task:1001"
# 获取到期任务
ZRANGEBYSCORE delay:queue 0 {current_timestamp}
# 移除已处理任务
ZREM delay:queue "task:1001"
4. 范围查询
Bash
# 价格范围查询
ZADD products:price 99 "product:A" 199 "product:B" 299 "product:C"
# 查询100-200价格区间商品
ZRANGEBYSCORE products:price 100 200 WITHSCORES
5. 权重队列
text
# 优先级队列(高分数优先)
ZADD priority:queue 10 "task:low" 50 "task:medium" 100 "task:high"
# 获取最高优先级任务
ZREVRANGE priority:queue 0 0
6. 最新列表(时间戳排序)
text
# 消息时间戳为分数
ZADD messages:recent 1700001000 "msg:1" 1700002000 "msg:2"
# 获取最新消息
ZREVRANGE messages:recent 0 10
与Set对比
| 特性 | ZSet | Set |
|---|---|---|
| 排序 | 按分数排序 | 无序 |
| 分数 | 有score | 无 |
| 范围查询 | 支持 | 不支持 |
| 排名查询 | 支持 | 不支持 |
| 内存占用 | 较大 | 较小 |
| 集合运算 | 不支持 | 支持 |
操作特点
O(1)操作
text
ZSCORE:获取分数
ZCARD:元素数量
ZINCRBY:增加分数
O(logN)操作
text
ZADD:添加元素
ZREM:删除元素
ZRANK/ZREVRANK:获取排名
O(logN + M)操作
text
ZRANGE/ZREVRANGE:范围获取
ZRANGEBYSCORE:分数范围
ZREMRANGEBYSCORE:删除范围
# M为返回/删除元素数量
内存优化
ziplist编码
text
# 调整阈值保持ziplist
zset-max-ziplist-entries 256
zset-max-ziplist-value 128
# 延迟转换,节省内存
分数精度
text
分数为double类型(64位浮点数)
整数分数内存效率更高
避免不必要的浮点分数
性能建议
范围查询优化
text
# 使用LIMIT分页
ZRANGEBYSCORE zset 0 100 LIMIT 0 20
# 减少返回元素数量
# 降低传输和内存开销
大ZSet遍历
text
# 使用ZSCAN分批遍历
ZSCAN leaderboard 0 COUNT 100
批量操作
text
# 批量添加
ZADD zset 1 "a" 2 "b" 3 "c"
# 比多次单独ZADD效率高
注意事项
分数重复
text
分数可以重复
同分数元素按字典序排列
排序稳定性需要注意
元素唯一性
text
# 重复添加更新分数
ZADD zset 100 "a"
ZADD zset 200 "a"
# 元素"a"分数变为200
编码转换
text
ziplist转skiplist不可逆
大ZSet内存占用较大
合理控制元素数量
编码对比
| 编码 | 条件 | 内存效率 | 查找效率 | 范围效率 |
|---|---|---|---|---|
| ziplist | ≤128元素且≤64字节 | 高 | O(N) | O(N) |
| skiplist | >128元素或>64字节 | 中 | O(logN) | O(logN+M) |
要点总结
- 有序集合是带分数排序的不重复集合
- ziplist编码适合小数据(≤128元素),内存高效
- skiplist编码适合大数据,跳表+哈希表实现
- 跳表O(logN)查找、插入、删除,支持高效范围查询
- 应用场景:排行榜、热搜、延时队列、范围查询、优先级队列
- ZRANK升序排名,ZREVRANK降序排名(从0开始)
- ZRANGEBYSCORE按分数范围查询,支持LIMIT分页
- 大ZSet使用ZSCAN遍历,避免阻塞
- 调整zset-max-ziplist-entries/value可延迟转换,优化内存
📝 发现内容有误?点击此处直接编辑