Redis Sorted Set(有序集合) · 旧版兼容命令至6. 过期、持久化与生命周期
本文按 Redis 8.2.10 核对编码与配置默认值;命令的引入版本另行标注。生产配置可能不同,可通过 CONFIG GET 查询。示例面向独立实验服务,运行结果和内存数字未在本次实测。
旧版兼容命令
| 命令与用途 | 示例与结果 | 复杂度 |
|---|---|---|
| ZRANGEBYSCORE 旧式分数区间查询 | ZRANGEBYSCORE learn:{zset}:a 10 20 WITHSCORES新代码用 ZRANGE ... BYSCORE。 | O(log N+M) |
| ZREVRANGE 旧式倒序名次查询 | ZREVRANGE learn:{zset}:a 0 1 WITHSCORES返回 carol、bob;新代码用 ZRANGE ... REV。 | O(log N+M) |
| ZREVRANGEBYSCORE 旧式倒序分数查询 | ZREVRANGEBYSCORE learn:{zset}:a 30 20 WITHSCORES上界在前;新代码用 ZRANGE ... BYSCORE REV。 | O(log N+M) |
| ZRANGEBYLEX 旧式字典序查询 | ZRANGEBYLEX learn:{zset}:lex [a [b同分集合返回 a、b;新代码用 ZRANGE ... BYLEX。 | O(log N+M) |
| ZREVRANGEBYLEX 旧式倒序字典序查询 | ZREVRANGEBYLEX learn:{zset}:lex [c [b返回 c、b;新代码用 ZRANGE ... BYLEX REV。 | O(log N+M) |
版本与复杂度补充
下表保留每条命令的引入版本、兼容状态及复杂度符号含义;复杂度不包含全部网络传输、内存分配和等待成本。
| 命令 | 引入版本与状态 | 复杂度说明 |
|---|---|---|
| ZADD | 1.2.0 | 每个成员 O(log N),N 为目标集合大小;多成员累加。 |
| ZINCRBY | 1.2.0 | O(log N),N 为集合大小;阻塞等待时间另计。 |
| ZSCORE | 1.2.0 | O(1)。大值复制、扩容、网络输出和等待时间需另外考虑。 |
| ZMSCORE | 6.2.0 | O(M),M 为指定或返回成员数。 |
| ZCARD | 1.2.0 | O(1)。大值复制、扩容、网络输出和等待时间需另外考虑。 |
| ZRANDMEMBER | 6.2.0 | O(M),M 为指定或返回成员数。 |
| ZSCAN | 2.8.0 | 官方渐进遍历口径:单次 O(1),完整迭代 O(N);COUNT 只是提示,编码和结果数量影响耗时。 |
| ZCOUNT | 2.0.0 | O(log N),N 为集合大小;阻塞等待时间另计。 |
| ZLEXCOUNT | 2.8.9 | O(log N),N 为集合大小;阻塞等待时间另计。 |
| ZRANK | 2.0.0 | O(log N),N 为集合大小;阻塞等待时间另计。 |
| ZREVRANK | 2.0.0 | O(log N),N 为集合大小;阻塞等待时间另计。 |
| ZRANGE | 1.2.0 | O(log N+M),N 为源集合大小,M 为返回或保存数;大 LIMIT 偏移还增加扫描成本。 |
| ZRANGESTORE | 6.2.0 | O(log N+M),N 为源集合大小,M 为返回或保存数;大 LIMIT 偏移还增加扫描成本。 |
| ZREM | 1.2.0 | O(M log N),N 为集合大小,M 为指定删除或弹出的成员数。 |
| ZREMRANGEBYRANK | 2.0.0 | O(log N+M),M 为实际移除数。 |
| ZREMRANGEBYSCORE | 1.2.0 | O(log N+M),M 为实际移除数。 |
| ZREMRANGEBYLEX | 2.8.9 | O(log N+M),M 为实际移除数。 |
| ZPOPMIN | 5.0.0 | O(M log N),N 为集合大小,M 为指定删除或弹出的成员数。 |
| ZPOPMAX | 5.0.0 | O(M log N),N 为集合大小,M 为指定删除或弹出的成员数。 |
| ZMPOP | 7.0.0 | O(K+M log N),K 为提供的键数,N 为选中集合大小,M 为弹出数。 |
| BZPOPMIN | 5.0.0 | O(log N),N 为集合大小;阻塞等待时间另计。 |
| BZPOPMAX | 5.0.0 | O(log N),N 为集合大小;阻塞等待时间另计。 |
| BZMPOP | 7.0.0 | O(K+M log N),K 为提供的键数,N 为选中集合大小,M 为弹出数。 |
| ZINTER | 6.2.0 | 最坏 O(Nmin×K+M log M),Nmin 为最小输入集合大小,K 为集合数,M 为结果大小。 |
| ZINTERCARD | 7.0.0 | 最坏 O(Nmin×K),Nmin 为最小集合大小,K 为集合数。 |
| ZINTERSTORE | 2.0.0 | 最坏 O(Nmin×K+M log M),Nmin 为最小输入集合大小,K 为集合数,M 为结果大小。 |
| ZUNION | 6.2.0 | O(T+M log M),T 为输入成员总数,M 为结果大小。 |
| ZUNIONSTORE | 2.0.0 | O(T+M log M),T 为输入成员总数,M 为结果大小。 |
| ZDIFF | 6.2.0 | 最坏 O(T+(N−M) log N),T 为输入成员总数,N 为第一个集合大小,M 为结果大小。 |
| ZDIFFSTORE | 6.2.0 | 最坏 O(T+(N−M) log N),T 为输入成员总数,N 为第一个集合大小,M 为结果大小。 |
| ZRANGEBYSCORE | 1.0.5;从 6.2.0 起弃用(兼容保留) | O(log N+M),N 为源集合大小,M 为返回或保存数;大 LIMIT 偏移还增加扫描成本。 |
| ZREVRANGE | 1.2.0;从 6.2.0 起弃用(兼容保留) | O(log N+M),N 为源集合大小,M 为返回或保存数;大 LIMIT 偏移还增加扫描成本。 |
| ZREVRANGEBYSCORE | 2.2.0;从 6.2.0 起弃用(兼容保留) | O(log N+M),N 为源集合大小,M 为返回或保存数;大 LIMIT 偏移还增加扫描成本。 |
| ZRANGEBYLEX | 2.8.9;从 6.2.0 起弃用(兼容保留) | O(log N+M),N 为源集合大小,M 为返回或保存数;大 LIMIT 偏移还增加扫描成本。 |
| ZREVRANGEBYLEX | 2.8.9;从 6.2.0 起弃用(兼容保留) | O(log N+M),N 为源集合大小,M 为返回或保存数;大 LIMIT 偏移还增加扫描成本。 |
ZRANGE 默认按排名,没有 BYRANK;BYSCORE、BYLEX、REV、LIMIT 从 6.2 起支持。BYLEX 系列示例使用同分成员,不能把不同分数集合当成全局字典序索引。
通用键操作见键与过期时间,这里集中列出类型自身的命令。
3. 底层编码与内存
小 ZSet 使用 listpack,超出条件后使用 skiplist 编码,对应跳表加字典:跳表支持排序和范围查询,字典支持成员定位。因此底层表示的查找成本也要区分操作。
8.2.10 默认 zset-max-listpack-entries 128、zset-max-listpack-value 64(成员字节长度)。超过任一条件可触发转换;不要把 128 当成集合最大容量,也不要假设删除成员后一定恢复紧凑编码。
编码与内存怎么测
在独立实验实例准备教学键,再读实际状态;下列是测量方法,本次未运行或填写测量数字:
ZADD learn:types:rank 10 alice 20 bob
OBJECT ENCODING learn:types:rank
MEMORY USAGE learn:types:rank固定键名、元素字节长度、Redis 版本和分配器,分别记录 1、10、100 个元素或不同值长度时的内存,再跨越编码阈值观察。单元素的增量开销可用相邻两次内存差估计,编码转换时不能套用线性平均;String/Bitmap 的横轴应使用字节数或最高位偏移。比较“一键多元素”和“多个独立键”时,把键名、对象、字典项和 TTL 管理开销一起计入。
MEMORY USAGE 默认对嵌套值采样,SAMPLES 0 会遍历所有嵌套值,仅在小型隔离实验中使用。其结果不等同于进程 RSS;大量键还会增加全局字典、分配器碎片和复制缓冲等成本,需结合 INFO memory。没有适用于所有版本的固定“每 key 几字节”。
依据:8.2.10 配置、对应类型实现、MEMORY USAGE。
4. 性能与阻塞风险
ZSCORE 是 O(1),ZINCRBY/排名查询是 O(log N),大范围查询和批量清理会随结果规模增长。ZRANGE LIMIT 大偏移可能需要走过许多成员,不能只看最终返回 10 条。ZSCAN 可用于无序维护,但不提供名次顺序或快照。热点榜单需限制请求范围与清理批次。
常规命令执行主要占用主执行路径,耗时计算会拖慢同实例其他请求;网络 I/O、后台持久化和释放内存有其他线程/进程参与。命令复杂度不包含所有网络、分配、编码转换与客户端成本。
5. 原子性与并发
ZINCRBY 原子增加分数;先查排名、再按结果修改涉及两次命令,需要 Lua 或 WATCH。匹配时“按分数取候选、校验、移除”应在同一脚本中完成,ZPOPMIN 不能替代任意分数区间匹配。原子领取延迟任务仍需要处理中租约和业务幂等来应对消费者崩溃。
MULTI/EXEC 保证排队命令连续执行,但执行时单条报错不会回滚其他已成功命令;WATCH 是乐观冲突检测,需要有界重试。Lua 也不会自动回滚运行错误前的写入,应先校验参数与类型,再修改状态。
6. 过期、持久化与生命周期
只有整键 TTL,没有成员 TTL。滑动窗口与在线心跳需显式删除过旧 score;ZRANGE 是查询,不会自动清理。按赛季或业务日分键后设置保留期。大范围 ZREMRANGEBYSCORE 也会占用执行时间,应结合批量策略或按时间分桶。
大键会增加 RDB/AOF 重写、复制全量同步和恢复时的处理量,fork 写时复制还可能增加峰值内存。单次同步删除聚合对象可能阻塞;UNLINK 先摘除键,把主要回收工作移到后台,但总回收工作和内存压力仍存在,不能替代限长和拆分。
完成教学实验后,只清理自己创建的键,例如 UNLINK learn:types:rank。
继续阅读
- 数据与原子性
- 数据与原子性
- 完整实践