ARTICLE DETAIL

资讯详情

深耕网站SEO优化与搜索引擎排名提升的一线实战洞察。

Redis SortedSet(ZSet,有序集合)详解

Redis SortedSet(ZSet,有序集合)详解 Redis SortedSetZSet有序集合详解SortedSet常简称 ZSet是 Redis 五大核心数据类型之一在 Set 集合「元素唯一」的基础上为每个元素关联一个双精度浮点型分数score并默认按照分数从小到大自动排序是 Redis 中最适合做排序、排名、范围筛选场景的数据结构。一、核心特性元素唯一性集合内的成员member不可重复和 Set 一致但分数score可以重复。自动有序性所有元素按 score 升序排列score 相同时按 member 的字典序lexicographical order排序。高效的查找与范围操作支持按排名、按分数区间快速获取元素时间复杂度远优于遍历。原子操作所有 ZSet 命令均为原子操作并发场景下无需额外加锁。灵活的排序维度score 可自定义为积分、时间戳、优先级、价格等任意数值实现不同业务的排序逻辑。与 Set、List 的对比数据类型元素唯一性排序依据重复元素典型场景Set是无无序不允许去重、交集并集运算List否插入顺序允许队列、栈、按插入顺序遍历ZSet是score 分数不允许排行榜、优先级队列、范围筛选二、底层数据结构与编码ZSet 底层采用两种编码方式自动切换兼顾内存效率与查询性能由 Redis 配置项控制转换阈值。1. 两种编码模式1压缩列表编码ziplist / listpack当 ZSet 同时满足以下两个条件时使用压缩列表存储极致节省内存元素数量 ≤zset-max-ziplist-entries默认值为128每个元素的 member 长度 ≤zset-max-ziplist-value默认值为64 字节压缩列表是一段连续的内存元素紧凑排列内存碎片极少缺点是插入、删除需要移动内存元素多了性能下降明显。2跳表哈希表编码skiplist dict当任意一个条件不满足时ZSet 自动转换为「跳表 哈希表」的组合结构这是 ZSet 的核心实现哈希表dict存储member - score的映射保证O(1)时间复杂度查询任意元素的分数。跳表skiplist按 score 有序存储所有元素支持平均 O(logN)、最坏 O(N)的插入、删除、范围查询性能接近平衡树但实现更简单、修改更灵活。注意编码转换是单向不可逆的从 ziplist 转为 skiplist 后即使元素减少也不会转回 ziplist避免频繁转换带来的性能损耗。2. 跳表的核心原理跳表是在有序链表基础上增加多层索引的结构最底层是完整的有序链表包含所有元素上层是下层的稀疏索引元素数量逐层减少查询时从最高层索引开始逐层向下逼近目标位置大幅减少比较次数。Redis 跳表默认最多 32 层元素越多索引效率越高相比红黑树跳表的范围查询更简单直观且插入删除无需旋转平衡实现成本更低。三、核心命令详解1. 元素添加与更新ZADD添加/更新元素基础语法ZADD key[NX|XX][CH][INCR]score member[score member...]NX仅当 member 不存在时才添加不更新已有元素XX仅当 member 已存在时才更新分数不新增元素CH返回值从「新增元素数量」变为「发生变化的元素数量」新增更新INCR对元素分数做增量操作等价于ZINCRBY示例向游戏排行榜添加3个玩家分数ZADD game:rank1000player1850player21200player3# 返回3新增3个元素仅更新 player2 的分数不新增ZADD game:rank XX900player2# 返回0XX模式下更新不计入默认返回值加CH则返回12. 元素删除命令作用示例ZREM key member [member...]删除指定成员ZREM game:rank player1ZREMRANGEBYRANK key start stop按排名范围删除升序从0开始ZREMRANGEBYRANK game:rank 0 2删除排名倒数前3ZREMRANGEBYSCORE key min max按分数范围删除ZREMRANGEBYSCORE game:rank 0 600删除低于600分的玩家ZREMRANGEBYLEX key min max按字典序范围删除需所有元素score相同ZREMRANGEBYLEX zset [a [c3. 基础查询命令作用示例ZSCORE key member获取指定元素的分数ZSCORE game:rank player3→1200ZRANK key member获取元素升序排名从0开始分数越小排名越靠前ZRANK game:rank player2→0ZREVRANK key member获取元素降序排名从0开始分数越大排名越靠前排行榜常用ZREVRANK game:rank player3→0ZCARD key获取集合元素总数ZCARD game:rank→3ZCOUNT key min max统计分数区间内的元素数量ZCOUNT game:rank 800 1000→2注意排名均从 0 开始计数业务中展示排名通常需要 1。4. 范围查询最核心功能1按排名范围获取升序获取分数从低到高ZRANGE key start stop[WITHSCORES]降序获取分数从高到低排行榜TopN最常用ZREVRANGE key start stop[WITHSCORES]start/stop排名下标从0开始支持负数-1 表示最后一个WITHSCORES同时返回元素和对应的分数示例获取分数最高的前2名玩家带分数ZREVRANGE game:rank01WITHSCORES# 1) player3# 2) 1200# 3) player1# 4) 10002按分数范围获取ZRANGEBYSCORE key min max[WITHSCORES][LIMIT offset count]ZREVRANGEBYSCORE key max min[WITHSCORES][LIMIT offset count]区间默认是闭区间加(表示开区间例如(100 200表示 100 score ≤ 200-inf表示负无穷inf表示正无穷常用于获取大于/小于某个值的所有元素LIMIT实现分页效果示例# 获取分数在 [800, 1200) 的玩家ZRANGEBYSCORE game:rank800(1200WITHSCORESRedis 5.0 之后ZRANGE命令增强可通过参数替代上述多个命令# 等价 ZRANGEBYSCOREZRANGE key min max BYSCORE WITHSCORES LIMIT010# 等价 ZREVRANGEZRANGE key0-1REV WITHSCORES5. 分数自增高频使用ZINCRBY原子增减元素分数ZINCRBY key increment member这是实现实时排行榜的核心命令原子性保证并发下数据准确。示例# player1 获得50分分数增加50ZINCRBY game:rank50player1# 返回10506. 集合运算ZSet 支持交集、并集运算结果存储到新的 ZSet 中支持加权与聚合方式配置。并集ZUNIONSTORE destination numkeys key[key...][WEIGHTS weight[weight...]][AGGREGATE SUM|MIN|MAX]交集ZINTERSTORE destination numkeys key[key...][WEIGHTS weight[weight...]][AGGREGATE SUM|MIN|MAX]WEIGHTS为每个集合的分数设置权重运算时分数先乘以对应权重AGGREGATE结果聚合方式默认SUM求和可选MIN取最小、MAX取最大示例计算日榜和周榜的并集作为总榜日榜权重1周榜权重2ZUNIONSTORE rank:total2rank:day rank:week WEIGHTS12四、典型业务应用场景1. 实时排行榜系统最经典的应用场景如游戏积分榜、文章点赞榜、商品销量榜、热搜榜用户行为触发时用ZINCRBY实时更新分数用ZREVRANGE快速获取 Top N用ZREVRANK查询用户当前排名2. 优先级队列将 score 作为任务优先级或执行时间戳高优先级任务设置更高的 score用ZREVRANGE取出优先级最高的任务执行延迟队列score 设为任务执行的时间戳定时轮询ZRANGEBYSCORE key -inf 当前时间戳取出到期任务3. 滑动窗口限流/统计以时间戳为 score用户操作ID为 member每次请求执行ZADD记录时间戳用ZREMRANGEBYSCORE清理窗口外的数据用ZCARD统计窗口内请求数判断是否触发限流4. 带权重的标签系统用户/商品的标签附带权重用于推荐、画像计算每个用户对应一个 ZSetmember 是标签score 是标签权重用ZINTERSTORE计算两个用户的标签交集与相似度实现协同推荐5. 范围筛选场景如按价格排序的商品列表、按时间排序的动态流支持按价格区间、时间区间快速筛选。五、性能优化与注意事项避免大 Key单个 ZSet 元素数量建议控制在万级以内百万级元素会成为大 Key删除、全量遍历会阻塞 Redis 主线程。业务上可按维度分片如按天、按用户组拆分多个 ZSet。注意分数精度score 是 IEEE 754 双精度浮点数整数精度仅保证到 2^53约 9×10^15超过此范围的整数会出现精度丢失。普通时间戳、积分场景足够超大数值需谨慎。集合运算慎用ZUNIONSTORE、ZINTERSTORE时间复杂度为 O(N)O(M log M)大集合运算耗时极长。生产环境尽量避免在主节点执行可放到从节点运算或提前预计算。相同分数的排序规则score 相同时按 member 字典序排序而非插入顺序。如果需要按插入时间排序可将时间戳拼接到 score 小数位或嵌入 member 中。合理利用 ziplist 编码对于小体量 ZSet如用户个人标签保持元素数量和 member 长度在阈值内可大幅节省内存不要随意调大zset-max-ziplist-entries否则插入性能会下降。六、实战示例游戏排行榜完整流程# 1. 初始化玩家分数ZADD game:rank1000Alice850Bob1200Charlie920David# 2. Bob 获胜增加100分ZINCRBY game:rank100Bob# 3. 查询 Charlie 的排名第1名从0开始ZREVRANK game:rank Charlie# 返回 0# 4. 获取排行榜前3名及分数ZREVRANGE game:rank02WITHSCORES# 1) Charlie 2) 1200# 3) Alice 4) 1000# 5) Bob 6) 950# 5. 查询800~1100分之间的玩家数量ZCOUNT game:rank8001100# 返回 3
返回列表