外观
Redis 的 zset 的定义?
⭐ 题目日期:
小米 - 2024/12/26
📝 题解:
Redis 的 ZSet(Sorted Set,有序集合) 是一种结合了「集合(Set)」和「排序(Sorted)」特性的数据结构。它通过为每个成员(member)关联一个分数(score),实现成员的唯一性且按分数自动排序。以下是 ZSet 的详细解析:
1. 核心特性
- 成员唯一性:
- 每个成员(member)唯一,但分数(score)可以重复。
- 若添加已存在的成员,会更新其分数并重新排序。
- 按分数排序:
- 默认按分数升序排列,支持按分数范围快速查询。
- 高性能操作:
- 插入、删除、查询的时间复杂度为 O(log N)(N 为成员数量),接近平衡树的效率。
2. 底层实现
ZSet 的底层采用两种数据结构,根据数据量动态切换:
(1) 压缩列表(Ziplist)
- 适用条件:
- 成员数量 ≤
zset-max-ziplist-entries
(默认 128)。 - 成员长度 ≤
zset-max-ziplist-value
(默认 64 字节)。
- 成员数量 ≤
- 特点:
- 内存紧凑,所有成员和分数连续存储。
- 适合小规模数据,节省内存。
(2) 跳跃表(Skiplist) + 哈希表(Hash Table)
- 跳跃表:
- 多层链表结构,支持快速范围查询和排名操作。
- 每个节点包含多个指针,实现类似二分查找的效果。
- 哈希表:
- 用于快速定位成员及其分数(时间复杂度 O(1))。
- 协同工作:
- 跳跃表负责排序和范围操作。
- 哈希表负责快速查找成员的分数。
3. 常用命令
命令 | 作用 | 示例 |
---|---|---|
ZADD key score member | 添加/更新成员及其分数 | ZADD leaderboard 1000 "Alice" |
ZRANGE key start stop | 按升序返回指定排名范围的成员 | ZRANGE leaderboard 0 2 WITHSCORES |
ZREVRANGE | 按降序返回指定排名范围的成员 | ZREVRANGE leaderboard 0 2 WITHSCORES |
ZRANK key member | 获取成员升序排名(从0开始) | ZRANK leaderboard "Alice" |
ZREVRANK | 获取成员降序排名 | ZREVRANK leaderboard "Alice" |
ZSCORE key member | 获取成员的分数 | ZSCORE leaderboard "Alice" |
ZRANGEBYSCORE | 按分数范围返回成员(支持分页) | ZRANGEBYSCORE leaderboard 800 1200 |
ZREM key member | 删除指定成员 | ZREM leaderboard "Bob" |
ZCOUNT | 统计分数范围内的成员数量 | ZCOUNT leaderboard 800 1200 |
ZCARD | 获取集合成员总数 | ZCARD leaderboard |
4. 典型应用场景
(1) 排行榜(Leaderboard)
- 示例:游戏玩家积分实时排序。
- 操作:
- 更新分数:
ZADD leaderboard 1500 "PlayerA"
。 - 获取前10名:
ZREVRANGE leaderboard 0 9 WITHSCORES
。 - 查询玩家排名:
ZREVRANK leaderboard "PlayerA"
。
- 更新分数:
(2) 延迟队列
- 示例:定时任务调度。
- 操作:
- 添加任务:
ZADD tasks <执行时间戳> "task_data"
。 - 轮询到期任务:
ZRANGEBYSCORE tasks 0 <当前时间戳> LIMIT 0 1
。
- 添加任务:
(3) 范围统计
- 示例:统计某时间段内的用户行为。
- 操作:
- 按时间戳存储事件:
ZADD events 1625000000 "login"
。 - 查询特定时间段的事件:
ZRANGEBYSCORE events 1625000000 1626000000
。
- 按时间戳存储事件:
(4) 优先级队列
- 示例:任务按优先级处理。
- 操作:
- 插入任务:
ZADD jobs 1 "low_priority_task"
。 - 获取最高优先级任务:
ZRANGE jobs 0 0
。
- 插入任务:
5. 高级功能
(1) 集合运算
ZUNIONSTORE
/ZINTERSTORE
:合并或交集多个 ZSet。- 注意:运算复杂度较高,可能阻塞 Redis,慎用于大数据量场景。
(2) 分数递增
ZINCRBY key increment member
:为成员的分数增加指定值。ZINCRBY leaderboard 50 "Alice" # Alice 分数增加50
6. 性能与内存优化
- 控制数据规模:
- 避免单个 ZSet 存储过多成员(如百万级),可能引发性能下降。
- 合理选择编码:
- 调整
zset-max-ziplist-entries
和zset-max-ziplist-value
,平衡内存与性能。
- 调整
- 避免大范围查询:
- 如
ZRANGE key 0 -1
可能阻塞 Redis,需分页操作。
- 如
7. 注意事项
- 分数精度:分数为双精度浮点数,可能存在精度丢失问题。
- 字典序排序:若分数相同,按成员字符串的二进制顺序排序。
- 内存占用:跳跃表结构的内存开销较大,需监控内存使用。
8. 总结
ZSet 是 Redis 中最灵活的数据结构之一,适用于需要排序和范围操作的场景。其底层通过 跳跃表 + 哈希表 的混合设计,兼顾了查询效率和排序功能。在使用时,需结合具体业务需求,合理设计分数规则,并关注性能与内存的平衡。