对于Redis:ZSet类型的解析

开篇介绍:

hello 大家,在我们系统学习完 Redis 五大基础数据类型中的 String(字符串)、List(列表)、Hash(哈希)、Set(集合) 之后,今天我们终于要迎来功能最完备、设计最精妙、实战价值最高、面试考察频率稳居第一 的数据结构 ------ZSet 有序集合(Sorted Set)。

如果说 String 是 Redis 的基础基石,List 是有序序列工具,Hash 是结构化对象载体,Set 是去重与交集工具,那么 ZSet 就是 Redis 为「排序、排名、范围查询、动态计分、多集合聚合 」这类核心业务场景量身打造的终极解决方案。在实际互联网开发中,只要你遇到排行榜、热度排序、积分排名、延时任务、优先级队列、范围筛选等需求,ZSet 几乎都是唯一且最优的选择,没有之一。

在正式进入命令、底层、实战场景之前,我们依旧先重申一个通用小技巧:如果你想在 redis-cli 中正常查看、输入中文而不出现十六进制转义字符,启动客户端时请携带 --raw 参数:

复制代码
redis-cli --raw

这是学习 Redis 所有数据类型都通用的细节,能让命令操作与结果查看更直观、更贴合日常开发习惯。


一、Redis ZSet 有序集合:核心基础认知

在学习任何命令、底层原理、实战用法之前,我们必须先把 ZSet 的本质定义、核心特性、铁律规则、与其他结构的区别彻底搞懂 ------ 这是理解后续所有内容的根基,也是避开使用误区、面试答对问题的关键。

1.1 什么是 ZSet(Sorted Set / 有序集合)?

Redis 官方对 ZSet(Sorted Set,有序集合) 的定义可以总结为两句话:

  1. 它保留了普通 Set 集合的核心特性:元素(member)唯一、不可重复;
  2. 它在 Set 基础上,为每一个元素绑定一个唯一关联的浮点型分数(score),Redis 会根据这个 score 自动维护元素的有序性。

简单来说:ZSet = 去重的 Set + 可排序的分数(score)

为了让大家秒懂,我们用两个最通俗的类比:

  • 班级考试排名:学生姓名(元素 member)唯一不能重复,考试分数(score)可以相同(并列排名),全班按分数从高到低 / 从低到高排序;
  • 三国武将武力榜 (你提供的教材示例):user:ranking 为 Key,武将名(关羽、张飞、夏侯惇...)是唯一元素,武力值(97.8、97.8、96.5...)是分数,整体按武力值有序排列。

示例:

复制代码
Key:user:ranking
元素(武将)    分数(武力值)
关羽           97.8
张飞           97.8
夏侯惇         96.5
周泰           96.2
甘宁           94.2
黄祖           94.0
严颜           93.2

从这个例子我们能一眼看出 ZSet 的核心特征:元素不重复、分数可重复、按分数自动有序。

1.2 ZSet 两大「铁律」

ZSet 的所有命令、底层实现、业务逻辑,都围绕两条不可动摇的铁律展开:

铁律 1:元素(member)唯一,绝对不可重复

  • 同一个 ZSet 中,同一个元素只能出现一次;
  • 若重复添加已存在的元素,不会新增元素,只会更新该元素对应的分数,并触发重新排序;
  • 这一点和普通 Set 完全一致,是 ZSet「去重能力」的核心。

铁律 2:分数(score)可重复,无任何唯一性限制

  • 多个不同元素可以绑定完全相同的分数(如关羽、张飞武力值都是 97.8);
  • 分数支持浮点数、整数、负数、正无穷(+inf)、负无穷(-inf);
  • 分数是 ZSet「有序能力」的唯一依据。

1.3 ZSet 的有序性:排序依据是「分数(score)」,而非下标 / 插入顺序

这是 ZSet 和 List 最核心的区别:

  • List 的有序 :依赖插入顺序 + 数组下标,和元素本身的值无关;
  • ZSet 的有序 :完全依赖分数(score),默认按分数 ** 升序(从小到大)** 排列,也可手动指定降序(从大到小);
  • 分数完全相同的元素,Redis 会按照元素的字典序(字母 / 汉字编码顺序) 做次要排序(业务开发中不建议强依赖此规则)。

1.4 分数(score)的完整规则:数据类型、合法取值、精度说明

ZSet 的 score 是 Redis 内部严格定义的类型,所有操作都必须遵循规则:

  1. 数据类型 :标准 64 位浮点数(double),兼容所有整数、小数;
  2. 合法取值 :
    • 普通数值:整数(1、100、-5)、小数(97.8、3.1415、-0.5);
    • 极限值:+inf(正无穷,代表最大分数)、-inf(负无穷,代表最小分数),常用于范围查询的边界;
  3. 精度注意 :double 浮点数有精度限制,不建议存储超大整数(超过 2^53),否则会出现精度丢失(零基础只需记住:日常业务的分数、积分、战力值完全够用);
  4. 分数修改 :支持覆盖更新(ZADD)、增量更新(ZINCRBY/ZADD INCR),是实现动态排行榜、实时计分的核心。

1.5 List / Set / ZSet 三者核心异同深度对比

数据结构 是否允许重复元素 是否有序 有序依据 核心能力 典型应用场景
List(列表) 允许重复 有序 插入顺序 + 索引下标 有序序列、首尾高效操作、阻塞队列 时间轴、消息队列、评论流、栈
Set(集合) 不允许重复 无序 无(无序) 去重、交集 / 并集 / 差集、随机抽取 标签、共同好友、UV 统计、黑名单
ZSet(有序集合) 不允许重复 有序 分数(score)+ 字典序 去重 + 排序、排名、范围查询、聚合 排行榜、热度榜、延时队列、积分系统

一句话总结三者差异:

  • 需要有序 + 重复 → 用 List;
  • 需要去重 + 无序 → 用 Set;
  • 需要去重 + 有序 + 排名 + 范围查询 → 用 ZSet。

1.6 ZSet 的最大容量与存储限制

和普通 Set 一致,单个 ZSet 最多可存储 2^32 − 1(约 42 亿) 个元素,完全满足互联网业务海量数据的存储需求,日常开发几乎不会遇到容量上限问题。

1.7 ZSet 的核心能力与不可替代性

ZSet 是 Redis 唯一同时具备以下 6 大核心能力的数据结构,这也是它不可替代的原因:

  1. 元素去重:和 Set 一样,自动过滤重复元素;
  2. 自动排序:基于分数实时维护有序性,无需手动排序;
  3. 排名查询:快速获取任意元素的升序 / 降序排名;
  4. 范围查询:按分数区间、排名区间快速筛选元素;
  5. 动态计分:原子性增量 / 覆盖修改分数,高并发下无竞争;
  6. 多集合聚合:支持交集、并集、加权聚合,实现多榜单合并。

二、ZSet 普通核心命令

ZSet 的命令数量多、功能细,但逻辑高度统一 ------所有命令都围绕「元素 + 分数」的增删改查、排序、排名、范围、删除展开。

2.1 ZADD:ZSet 最核心写入命令(添加 / 更新元素 + 分数)

2.1.1 核心作用

向指定 ZSet 中添加新元素并绑定分数;若元素已存在,则覆盖更新其分数并重新排序;分数支持 double、+inf、-inf。

2.1.2 完整语法

复制代码
ZADD key [NX | XX] [GT | LT] [CH] [INCR] score member [score member ...]

2.1.3 有效版本

1.2.0 及以上

2.1.4 时间复杂度

O (log (N)) per member(单个元素),批量添加为 O (k・log (N)),k 为添加元素个数。

通俗解释:ZSet 底层用跳表实现,插入 / 更新的时间复杂度是对数级,性能极高,即使千万级元素也能快速操作。

2.1.5 返回值含义

  • 默认:返回本次新增成功的元素个数(更新已存在元素不计入);
  • 带 CH 选项:返回新增 + 更新的总元素个数;
  • 带 INCR 选项:返回增量计算后的最终分数(字符串格式)。

2.1.6 核心选项详解

ZADD 的选项是 ZSet 最核心的高级功能,覆盖「安全添加、安全更新、增量修改、计数统计」,逐一解释:

  1. XX :仅更新已存在的元素,绝不新增;元素不存在则直接跳过,不执行任何操作;
  2. NX :仅新增不存在的元素,绝不更新;元素已存在则直接跳过,不修改分数;
  3. GT :仅当新分数 > 旧分数时,才更新(大于才覆盖,用于只升不降的计分,如积分、战力);
  4. LT :仅当新分数 < 旧分数时,才更新(小于才覆盖,用于只降不升的计分);
  5. CH:修改返回值统计规则,默认只算新增,加 CH 后「新增 + 更新」都统计;
  6. INCR :增量修改分数(等价于 ZINCRBY),一次只能操作 1 个元素,返回新分数。

2.1.7 逐行实操示例

bash 复制代码
# 1. 添加元素 "one",分数 1 → 新增成功,返回 1
redis> ZADD myzset 1 "one"
(integer) 1

# 2. 添加元素 "uno",分数 1 → 新增成功,返回 1
redis> ZADD myzset 1 "uno"
(integer) 1

# 3. 批量添加:two=2、three=3 → 新增 2 个,返回 2
redis> ZADD myzset 2 "two" 3 "three"
(integer) 2

# 4. 查询全量元素(升序)+ 分数(WITHSCORES)
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "one"
2) "1"
3) "uno"
4) "1"
5) "two"
6) "2"
7) "three"
8) "3"

# 5. 更新已存在元素:one=10、two=20、three=30 → 无新增,返回 0
redis> ZADD myzset 10 one 20 two 30 three
(integer) 0

# 6. 再次查询,分数已更新
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "uno"
2) "1"
3) "one"
4) "10"
5) "two"
6) "20"
7) "three"
8) "30"

# 7. 加 CH 选项:更新 one=100、two=200、three=300 → 3 个更新,返回 3
redis> ZADD myzset CH 100 one 200 two 300 three
(integer) 3

# 8. 加 XX 选项:仅更新已存在元素,four/five 不存在 → 无操作,返回 0
redis> ZADD myzset XX 1 one 2 two 3 three 4 four 5 five
(integer) 0

# 9. 加 NX 选项:仅新增,one/two/three 已存在,仅新增 four、five → 返回 2
redis> ZADD myzset NX 100 one 200 two 300 three 400 four 500 five
(integer) 2

# 10. 加 INCR 选项:one 分数 +10(1+10=11),返回新分数
redis> ZADD myzset INCR 10 one
"11"

# 11. 添加负无穷、正无穷元素 → 新增 2 个,返回 2
redis> ZADD myzset -inf "negative infinity" +inf "positive infinity"
(integer) 2

# 12. 最终全量查询,包含 ±inf 元素
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "negative infinity"
2) "-inf"
3) "uno"
4) "1"
5) "two"
6) "2"
7) "three"
8) "3"
9) "one"
10) "11"
11) "four"
12) "400"
13) "five"
14) "500"
15) "positive infinity"
16) "inf"

2.1.8 常见误区与注意事项

  1. 重复添加同一元素 = 覆盖分数,不会报错,也不会新增元素;
  2. INCR 选项一次只能操作 1 个元素,批量会报错;
  3. +inf/-inf 是合法分数,常用于范围查询的「全量匹配」;
  4. 分数为浮点数时,Redis 会自动存储,无需额外转换。

2.1.9 实战业务场景

  • 初始化排行榜:ZADD user:ranking:20260208 97.8 关羽 97.8 张飞;
  • 安全新增(不覆盖旧分数):ZADD rank NX 100 user1001;
  • 只升不降计分(积分):ZADD rank GT 105 user1001;
  • 实时更新榜单分数:ZADD rank 88 user1001。

2.2 ZCARD:获取 ZSet 元素总个数(基数)

2.2.1 核心作用

查询指定 ZSet 中元素的总数量(和 Set 的 SCARD 完全一致),Key 不存在返回 0。

2.2.2 完整语法

复制代码
ZCARD key

2.2.3 有效版本

1.2.0 及以上

2.2.4 时间复杂度

O (1)(Redis 内部维护长度计数器,无需遍历,性能极致)

2.2.5 返回值含义

整数:ZSet 中的元素个数;Key 不存在返回 0。

2.2.6 逐行实操示例

bash 复制代码
# 1. 添加 2 个元素
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1

# 2. 查询总个数 → 返回 2
redis> ZCARD myzset
(integer) 2

2.2.7 实战业务场景

  • 统计榜单总参与人数:ZCARD user:ranking:20260208;
  • 统计延时任务队列长度:ZCARD delay:task:queue。

2.3 ZCOUNT:统计指定分数区间内的元素个数

2.3.1 核心作用

查询分数在 [min, max] 区间内的元素数量;默认包含 min 和 max,用 ( 前缀表示开区间(不包含),这里的min和max是指分数,可不是下标哦

2.3.2 完整语法

复制代码
ZCOUNT key min max

2.3.3 有效版本

2.0.0 及以上

2.3.4 时间复杂度

O (log (N))(先定位分数边界,再计数,性能极高)

2.3.5 返回值含义

整数:满足分数区间条件的元素个数。

2.3.6 区间规则(重点)

  • 1 3:闭区间,包含 1 和 3;
  • (1 3:开区间左,不包含 1,包含 3;
  • (1 (3:全开区间,不包含 1 和 3;
  • -inf +inf:匹配所有元素(全量计数)。

2.3.7 逐行实操示例

bash 复制代码
# 1. 添加 3 个元素:one=1、two=2、three=3
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1

# 2. 全量计数(-inf 到 +inf)→ 返回 3
redis> ZCOUNT myzset -inf +inf
(integer) 3

# 3. [1,3] → 返回 3
redis> ZCOUNT myzset 1 3
(integer) 3

# 4. (1,3] → 不包含 1,返回 2(two、three)
redis> ZCOUNT myzset (1 3
(integer) 2

# 5. (1,3) → 全开区间,返回 1(two)
redis> ZCOUNT myzset (1 (3
(integer) 1

2.3.8 实战业务场景

  • 统计考试 60~80 分的学生人数:ZCOUNT exam:score 60 80;
  • 统计热度 1000~5000 的内容数量:ZCOUNT content:hot 1000 5000;
  • 统计武力值 90~100 的武将数量:ZCOUNT user:ranking 90 100。

2.4 ZRANGE:按下标区间查询(升序:分数从小到大)

2.4.1 核心作用

按 ** 升序(分数小 → 大)** 返回指定下标区间的元素;支持 WITHSCORES 同时返回分数;下标从 0 开始,支持负数(-1 = 最后一个元素,带有负号就是指倒数~),注意这里要填入的数字就是下标了哦,那么我们要是输入0和-1就是指将全部都显示出来

2.4.2 完整语法

bash 复制代码
ZRANGE key start stop [WITHSCORES]

2.4.3 有效版本

1.2.0 及以上

2.4.4 时间复杂度

O (log (N) + M),N=ZSet 总元素数,M = 返回元素数(查询性能极高)。

2.4.5 返回值含义

  • 不带 WITHSCORES:元素列表;
  • 带 WITHSCORES:元素 + 分数交替返回的列表。

2.4.6 逐行实操示例

bash 复制代码
# 1. 添加 3 个元素
redis> ZADD myzset 1 "one" 2 "two" 3 "three"
(integer) 3

# 2. 全量查询 + 分数
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "one"
2) "1"
3) "two"
4) "2"
5) "three"
6) "3"

# 3. 仅查询元素(无分数)
redis> ZRANGE myzset 0 -1
1) "one"
2) "two"
3) "three"

# 4. 下标 2~3 → 仅 three
redis> ZRANGE myzset 2 3
1) "three"

# 5. 倒数 2 个元素(-2~-1)→ two、three
redis> ZRANGE myzset -2 -1
1) "two"
2) "three"

2.4.7 实战业务场景

  • 查询榜单末尾 N 个元素(分数最低);
  • 榜单分页查询(升序);
  • 小范围遍历 ZSet 元素。

2.5 ZREVRANGE:按下标区间查询(降序:分数从大到小)

2.5.1 核心作用(详细版)

ZREVRANGE 是 ZRANGE 命令的逆序增强版本,核心作用是严格按照有序集合(ZSet)中元素的分数从大到小(降序)排序,精准返回指定下标区间内的所有元素;支持携带元素对应的分数一起返回,是实现 "排行榜 TOP N 展示、高分元素优先获取" 类业务的核心命令,也是生产环境中最常用的 ZSet 查询命令之一。

该命令的下标规则与数组下标逻辑完全一致:下标从 0 开始(0 对应降序后的第一个元素,即分数最大的元素),-1 代表最后一个元素(分数最小的元素),-2 代表倒数第二个元素,以此类推;查询区间为左闭右闭,即 start 和 stop 对应的元素都会被包含在结果中。

2.5.2 备注

Redis 6.2.0+ 版本中,官方建议使用 ZRANGE key start stop REV [WITHSCORES] 替代 ZREVRANGE(功能完全等价),但由于 Redis 5.x、4.x 等低版本仍在生产环境广泛部署,ZREVRANGE 仍是开发者必须熟练掌握的基础命令;低版本 Redis 无此替代方案,只能使用 ZREVRANGE。

2.5.3 完整语法
复制代码
ZREVRANGE key start stop [WITHSCORES]
  • key:必填,目标有序集合的键名(需确保键存在且类型为 ZSet,否则返回空数组);
  • start:必填,起始下标(整数,支持负数,如 0 代表第一个元素,-1 代表最后一个元素);
  • stop:必填,结束下标(整数,支持负数,如 9 代表第 10 个元素,-2 代表倒数第二个元素);
  • WITHSCORES:可选,若添加此参数,返回结果会同时包含元素名称和对应的分数;若不添加,仅返回元素名称。
2.5.4 有效版本

1.2.0 及以上(全版本兼容,无兼容性风险)

2.5.5 逐行实操示例
复制代码
# 1. 向有序集合 myzset 中添加 3 个元素(格式:分数 元素值)
redis> ZADD myzset 1 "one" 2 "two" 3 "three"
(integer) 3  # 返回值:成功添加的元素个数(无重复元素时等于输入数量)

# 2. 降序查询全量元素并携带分数(0 到 -1 覆盖所有元素,WITHSCORES 携带分数)
redis> ZREVRANGE myzset 0 -1 WITHSCORES
1) "three"   # 第一个元素(分数 3,降序第一)
2) "3"       # three 对应的分数(字符串格式返回)
3) "two"     # 第二个元素(分数 2)
4) "2"       # two 对应的分数
5) "one"     # 第三个元素(分数 1,降序最后)
6) "1"       # one 对应的分数

# 3. 仅查询降序全量元素(不携带分数)
redis> ZREVRANGE myzset 0 -1
1) "three"
2) "two"
3) "one"

# 补充示例:查询降序 TOP1 元素(仅第一个元素)
redis> ZREVRANGE myzset 0 0 WITHSCORES
1) "three"
2) "3"

# 补充示例:下标超出范围时(无报错,仅返回存在的元素)
redis> ZREVRANGE myzset 0 100 WITHSCORES
1) "three"
2) "3"
3) "two"
4) "2"
5) "one"
6) "1"
2.5.6 返回值解析

ZREVRANGE 的返回值为数组(列表)类型,具体格式分两种情况:

  1. 未添加 WITHSCORES 参数:返回一维字符串数组,数组元素为符合下标区间的 ZSet 元素名称,顺序严格为分数从大到小;若目标 ZSet 为空、不存在,或无符合条件的元素,返回空数组(())。
  2. 添加 WITHSCORES 参数:返回 "交替排列" 的一维数组(逻辑上是二维,存储为一维),格式为「元素 1、元素 1 分数、元素 2、元素 2 分数......」,所有分数均以字符串形式返回(Redis 中数值类返回值默认存储为字符串);无符合条件元素时返回空数组。
2.5.7 关键细节说明
  • 下标容错:start/stop 超出 ZSet 实际下标范围时,不会抛出异常,只会返回存在的元素(如 ZREVRANGE myzset 0 100 仅返回所有元素);
  • 空键处理:若目标 key 不存在(或不是 ZSet 类型),命令返回空数组,无报错;
  • 性能特性:时间复杂度为 O(log(N)+M),其中 N 是 ZSet 总元素数,M 是返回的元素数;查询少量元素(如 TOP10)时性能极高,适合高并发场景;查询全量元素时需注意耗时(与元素总数正相关)。
2.5.8 实战业务场景(详细版)
  1. 游戏战力排行榜:查询全服战力 TOP10 玩家,命令为 ZREVRANGE game:ranking:power 0 9 WITHSCORES,返回结果可直接展示 "玩家 ID + 战力值";
  2. 电商销量榜单:查询 2026 年 2 月销量最高的 20 个商品,命令为 ZREVRANGE shop:ranking:sales:202602 0 19 WITHSCORES;
  3. 内容热度榜单:查询当前平台热度最高的 50 篇文章,命令为 ZREVRANGE content:ranking:hot 0 49 WITHSCORES;
  4. 考试成绩排名:查询班级数学成绩前 10 名的学生,命令为 ZREVRANGE class:1:math:score 0 9 WITHSCORES;
  5. 积分排行榜:查询用户积分降序前 5 名,命令为 ZREVRANGE user:ranking:points 0 4 WITHSCORES。

2.6 ZRANGEBYSCORE:按分数区间查询(升序)

2.6.1 核心作用(详细版)

ZRANGEBYSCORE 是有序集合中按分数维度精准筛选元素的核心命令 ,核心作用是按照元素分数从小到大(升序)的顺序,返回指定分数区间内的所有元素;支持闭区间(默认)、开区间(使用 ( 标识),还支持无限区间(-inf 代表负无穷,+inf 代表正无穷),是实现 "分数范围筛选、条件过滤" 类业务的核心命令。

与 ZREVRANGE 最大的区别是:ZRANGEBYSCORE 的筛选维度是 "分数" 而非 "下标",开发者只需关注元素的分数范围,无需关心元素在 ZSet 中的下标位置。

2.6.2 备注

Redis 6.2.0+ 版本中,官方建议使用 ZRANGE key BYSCORE min max [WITHSCORES] 替代 ZRANGEBYSCORE(功能完全一致),但低版本 Redis(5.x 及以下)仍需使用 ZRANGEBYSCORE,是生产环境的主流用法;该命令支持分页(LIMIT 参数),可避免返回大量元素导致的性能问题。

2.6.3 完整语法
复制代码
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
  • key:必填,目标有序集合的键名;
  • min:必填,分数区间下限,支持整数 / 浮点数、-inf(负无穷)、(数值(开区间,不包含该数值);
  • max:必填,分数区间上限,支持整数 / 浮点数、+inf(正无穷)、(数值(开区间,不包含该数值);
  • WITHSCORES:可选,添加后返回元素名称 + 对应分数,否则仅返回元素名称;
  • LIMIT offset count:可选(Redis 2.0.0 + 支持),分页参数,offset 为偏移量,count 为每页数量,用于避免返回大量元素。
2.6.4 逐行实操示例
复制代码
# 1. 向 myzset 添加 3 个元素(分数 1、2、3)
redis> ZADD myzset 1 "one" 2 "two" 3 "three"
(integer) 3  # 返回值:成功添加的元素个数

# 2. 查询分数区间「负无穷到正无穷」的所有元素(全量),升序排列
redis> ZRANGEBYSCORE myzset -inf +inf
1) "one"   # 分数 1(最小)
2) "two"   # 分数 2
3) "three" # 分数 3(最大)

# 3. 查询分数区间 [1,2] 的元素(闭区间,包含 1 和 2)
redis> ZRANGEBYSCORE myzset 1 2
1) "one"
2) "two"

# 4. 查询分数区间 (1,2] 的元素(开区间,不包含 1,仅包含 2)
redis> ZRANGEBYSCORE myzset (1 2
1) "two"

# 补充示例:查询分数 ≥2 且 ≤3 的元素并携带分数
redis> ZRANGEBYSCORE myzset 2 3 WITHSCORES
1) "two"
2) "2"
3) "three"
4) "3"

# 补充示例:查询分数 <3 的元素(开区间上限)
redis> ZRANGEBYSCORE myzset -inf (3
1) "one"
2) "two"

# 补充示例:分页查询分数 1~3 的元素,每页 2 条,取第 2 页
redis> ZRANGEBYSCORE myzset 1 3 LIMIT 2 2
1) "three"
2.6.5 返回值解析

ZRANGEBYSCORE 的返回值为数组(列表)类型,规则与 ZREVRANGE 完全一致:

  1. 未添加 WITHSCORES:返回一维字符串数组,元素为符合分数区间的 ZSet 元素名称,顺序为分数从小到大;无符合条件元素时返回空数组。
  2. 添加 WITHSCORES:返回交替排列的一维数组(元素 1、元素 1 分数、元素 2、元素 2 分数......),分数以字符串形式返回;无符合条件元素时返回空数组。
  3. 添加 LIMIT:返回结果会按分页规则截取,若偏移量超出元素总数,返回空数组。
2.6.6 关键细节说明
  • 区间规则:默认所有区间为闭区间(包含边界值),若需排除边界值,需在数值前加 ( 符号(如 (1 代表大于 1,(3 代表小于 3);
  • 无限区间:-inf 和 +inf 无需加引号,直接使用即可,分别代表 "最小分数" 和 "最大分数";
  • 分页注意:LIMIT 参数的时间复杂度会增加 O(M)(M 为 offset+count),偏移量过大时(如 LIMIT 10000 10)会有性能损耗,需谨慎使用;
  • 空键处理:目标 key 不存在或非 ZSet 类型时,返回空数组,无报错;
  • 性能特性:时间复杂度为 O(log(N)+M),N 为 ZSet 总元素数,M 为返回的元素数;返回少量元素时性能极高,返回大量元素时需结合分页。
2.6.7 实战业务场景(详细版)
  1. 学生成绩筛选:查询班级数学成绩 90~100 分的学生(闭区间),命令为 ZRANGEBYSCORE class:1:math:score 90 100 WITHSCORES;
  2. 内容热度筛选:查询平台热度 > 1000 的文章(开区间下限),命令为 ZRANGEBYSCORE content:ranking:hot (1000 +inf WITHSCORES;
  3. 游戏武将筛选:查询武力值 95~100 的武将(闭区间),命令为 ZRANGEBYSCORE game:general:power 95 100 WITHSCORES;
  4. 积分筛选:查询用户积分≥5000 且 < 10000 的会员(半开半闭区间),命令为 ZRANGEBYSCORE user:vip:points 5000 (10000 WITHSCORES;
  5. 分页查询高分学生:查询 80~100 分的学生,每页 10 条,取第 2 页,命令为 ZRANGEBYSCORE class:1:math:score 80 100 LIMIT 10 10 WITHSCORES;
  6. 低质内容清理前置筛选:查询热度 < 10 的内容(用于后续删除),命令为 ZRANGEBYSCORE content:ranking:hot -inf (10。

2.7 ZPOPMAX:删除并返回分数最高的 N 个元素

2.7.1 核心作用(详细版)

ZPOPMAX 是有序集合的 "弹出类" 核心命令,核心作用是原子性地删除有序集合中分数最高的一个或多个元素,并返回被删除的元素(及对应分数);支持指定弹出元素的个数(count),是实现 "优先级队列、任务调度、抽奖活动" 等业务的核心命令。

该命令的 "原子性" 是关键特性:删除和返回操作是一个不可分割的整体,即使高并发场景下,也不会出现 "元素被多个客户端同时获取" 的问题;若指定的 count 大于 ZSet 中的元素总数,只会删除并返回所有元素。

2.7.2 完整语法
复制代码
ZPOPMAX key [count]
  • key:必填,目标有序集合的键名;
  • count:可选,需要弹出的元素个数(正整数),默认值为 1(仅弹出分数最高的 1 个元素)。
2.7.3 有效版本

5.0.0 及以上(低版本 Redis 无此命令,需通过 ZREVRANGE + ZREM 模拟,但无原子性保证)

2.7.4 逐行实操示例
复制代码
# 1. 向 myzset 添加 3 个元素(分数 1、2、3)
redis> ZADD myzset 1 "one" 2 "two" 3 "three"
(integer) 3  # 返回值:成功添加的元素个数

# 2. 弹出分数最高的 1 个元素(默认 count=1)
redis> ZPOPMAX myzset
1) "three"   # 被删除的元素名称
2) "3"       # 被删除元素的分数(字符串格式)

# 3. 查看弹出后的 ZSet(仅剩 one、two)
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "one"
2) "1"
3) "two"
4) "2"

# 补充示例:弹出分数最高的 2 个元素
redis> ZPOPMAX myzset 2
1) "two"   # 第一个被删除的元素(分数 2)
2) "2"
3) "one"   # 第二个被删除的元素(分数 1)
4) "1"

# 补充示例:count 大于元素总数(无报错,返回所有元素)
redis> ZPOPMAX myzset 10
(empty array) # ZSet 已空,返回空数组
2.7.5 返回值解析

ZPOPMAX 的返回值为数组(列表)类型,规则如下:

  1. 未指定 count(默认 count=1):返回交替排列的一维数组,格式为「被删除元素、元素分数」;若 ZSet 为空,返回空数组。
  2. 指定 count(count≥1):返回交替排列的一维数组,格式为「元素 1、元素 1 分数、元素 2、元素 2 分数......」,元素顺序为分数从高到低;若 ZSet 为空,返回空数组;若 count 大于元素总数,仅返回所有被删除的元素。所有分数均以字符串形式返回。
2.7.6 关键细节说明
  • 原子性:删除和返回操作是原子的,高并发下无竞态条件(如多个客户端同时执行 ZPOPMAX,不会获取到同一个元素);
  • 空键处理:目标 key 不存在或为空 ZSet 时,返回空数组,无报错;
  • 性能特性:时间复杂度为 O(log(N)+M),N 为 ZSet 总元素数,M 为弹出的元素数;弹出少量元素时性能极高;
  • 低版本兼容:5.0.0 以下版本需模拟实现(ZREVRANGE key 0 count-1 WITHSCORES + ZREM key 元素1 元素2...),但此方式非原子性,高并发下可能出错。
2.7.7 实战业务场景(详细版)
  1. 优先级任务队列:高优先级任务(分数高)先执行,每次弹出 1 个最高优先级任务,命令为 ZPOPMAX task:queue:priority,获取后执行任务逻辑;
  2. 抽奖活动:抽奖榜单中分数最高的用户中奖,中奖后移除该用户,命令为 ZPOPMAX lottery:ranking 1;
  3. 限时秒杀资格发放:按用户预约优先级(分数)发放前 10 个秒杀资格,命令为 ZPOPMAX seckill:reserve 10;
  4. 任务调度:按任务紧急程度(分数)处理前 5 个任务,命令为 ZPOPMAX task:queue:urgent 5;
  5. 排行榜清零:弹出并记录排行榜所有元素(用于数据归档),命令为 ZPOPMAX game:ranking:daily 10000(count 设为足够大)。

2.8 BZPOPMAX:ZPOPMAX 的阻塞版本

2.8.1 核心作用(详细版)

BZPOPMAX 是 ZPOPMAX 的阻塞增强版本,核心作用是原子性地弹出指定有序集合中分数最高的元素;若所有指定的 ZSet 均为空,客户端会阻塞等待,直到有元素被添加到任意一个 ZSet 中,或等待时间达到超时阈值。

该命令是实现 "分布式阻塞优先级队列" 的核心:多个客户端可同时监听同一个 / 多个 ZSet,当有元素添加时,只有一个客户端能原子性地获取到该元素,完美解决分布式场景下的任务调度问题;支持监听多个 ZSet,按参数顺序优先弹出第一个非空 ZSet 的最高分数元素。

2.8.2 完整语法
复制代码
BZPOPMAX key [key ...] timeout
  • key [key ...]:必填,一个或多个目标有序集合的键名(按顺序优先级监听);
  • timeout:必填,阻塞超时时间(单位:秒),支持浮点数(如 0.5 代表 500 毫秒);若设为 0,代表永久阻塞(直到有元素弹出)。
2.8.3 逐行实操示例
复制代码
# 1. 清空待监听的 ZSet(确保初始为空)
redis> DEL zset1 zset2
(integer) 0  # 返回值:被删除的键个数(无键则为0)

# 2. 向 zset1 添加 3 个元素(分数 0、1、2)
redis> ZADD zset1 0 "a" 1 "b" 2 "c"
(integer) 3  # 返回值:成功添加的元素个数

# 3. 阻塞弹出最大值(监听 zset1、zset2,timeout=0 永久阻塞)
redis> BZPOPMAX zset1 zset2 0
1) "zset1"   # 弹出元素所属的 ZSet 键名
2) "c"       # 被弹出的元素名称
3) "2"       # 被弹出元素的分数(字符串格式)

# 补充示例:监听空 ZSet,设置超时时间 5 秒(5秒后返回空)
redis> BZPOPMAX zset1 zset2 5
(nil) # 超时后返回 nil,无元素弹出

# 补充示例:多 ZSet 监听(zset2 有元素时优先弹出 zset1?不,按参数顺序)
redis> ZADD zset2 10 "x"
(integer) 1
redis> BZPOPMAX zset1 zset2 0
1) "zset2"   # zset1 为空,弹出 zset2 的最高分数元素
2) "x"
3) "10"
2.8.4 返回值解析

BZPOPMAX 的返回值分两种情况:

  1. 成功弹出元素:返回一维数组,格式为「所属 ZSet 键名、被弹出元素名称、元素分数」,分数以字符串形式返回;
  2. 超时无元素:返回 nil(空),代表阻塞时间达到 timeout 阈值,且所有监听的 ZSet 仍为空。
2.8.5 关键细节说明
  • 监听优先级:按参数中 key 的顺序监听,优先弹出第一个非空 ZSet 的最高分数元素(即使后续 ZSet 有更高分数的元素);
  • 原子性:弹出操作是原子的,多个客户端监听同一个 ZSet 时,只有一个客户端能获取到元素;
  • 阻塞特性:客户端阻塞期间,不会占用 Redis 主线程资源(Redis 采用 IO 多路复用处理阻塞客户端);
  • 超时规则:timeout 设为 0 时永久阻塞,直到有元素弹出;设为正数时,超时后返回 nil,客户端解除阻塞;
  • 空键处理:监听的 key 不存在时,视为空 ZSet,继续监听下一个 key。
2.8.6 实战业务场景(详细版)
  1. 分布式优先级任务队列:多台服务器监听同一个任务队列,高优先级任务到达时,其中一台服务器原子性获取并执行,命令为 BZPOPMAX task:queue:priority 0;
  2. 多队列任务调度:监听 "紧急任务队列" 和 "普通任务队列",优先处理紧急任务,命令为 BZPOPMAX task:queue:urgent task:queue:normal 0;
  3. 实时消息推送:按消息优先级(分数)推送,无消息时阻塞等待,命令为 BZPOPMAX msg:queue:priority 0;
  4. 秒杀活动库存处理:监听秒杀资格队列,有资格用户时立即处理,无用户时阻塞等待,命令为 BZPOPMAX seckill:queue 60(超时 60 秒后退出)。

2.9 ZPOPMIN:删除并返回分数最低的元素

2.9.1 核心作用(详细版)

ZPOPMIN 是 ZPOPMAX 的反向命令,核心作用是原子性地删除有序集合中分数最低的一个或多个元素,并返回被删除的元素(及对应分数);支持指定弹出元素的个数(count),适用于 "低优先级任务清理、低分元素淘汰" 等场景。

该命令同样具备原子性:删除和返回操作不可分割,高并发下无竞态条件;若指定的 count 大于 ZSet 中的元素总数,只会删除并返回所有元素。

2.9.2 完整语法
复制代码
ZPOPMIN key [count]
  • key:必填,目标有序集合的键名;
  • count:可选,需要弹出的元素个数(正整数),默认值为 1(仅弹出分数最低的 1 个元素)。
2.9.3 逐行实操示例
复制代码
# 1. 向 myzset 添加 3 个元素(分数 1、2、3)
redis> ZADD myzset 1 "one" 2 "two" 3 "three"
(integer) 3  # 返回值:成功添加的元素个数

# 2. 弹出分数最低的 1 个元素(默认 count=1)
redis> ZPOPMIN myzset
1) "one"    # 被删除的元素名称
2) "1"      # 被删除元素的分数(字符串格式)

# 3. 查看弹出后的 ZSet(仅剩 two、three)
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "two"
2) "2"
3) "three"
4) "3"

# 补充示例:弹出分数最低的 2 个元素
redis> ZPOPMIN myzset 2
1) "two"    # 第一个被删除的元素(分数 2)
2) "2"
3) "three"  # 第二个被删除的元素(分数 3)
4) "3"

# 补充示例:count 大于元素总数(返回空数组)
redis> ZPOPMIN myzset 10
(empty array)
2.9.4 返回值解析

ZPOPMIN 的返回值规则与 ZPOPMAX 完全一致:

  1. 未指定 count:返回「被删除元素、元素分数」的一维数组;ZSet 为空时返回空数组。
  2. 指定 count:返回「元素 1、元素 1 分数、元素 2、元素 2 分数......」的一维数组,元素顺序为分数从低到高;ZSet 为空时返回空数组。所有分数均以字符串形式返回。
2.9.5 关键细节说明
  • 原子性:删除 + 返回操作原子化,高并发下无重复获取问题;
  • 低版本兼容:5.0.0 以下版本需通过 ZRANGE key 0 count-1 WITHSCORES + ZREM 模拟,无原子性;
  • 性能特性:时间复杂度 O(log(N)+M),弹出少量元素时性能极高;
  • 空键处理:目标 key 不存在或为空 ZSet 时,返回空数组,无报错。
2.9.6 实战业务场景(详细版)
  1. 低优先级任务清理:删除并处理分数最低的 10 个低优先级任务,命令为 ZPOPMIN task:queue:low 10;
  2. 榜单低分元素淘汰:淘汰排行榜中分数最低的 50 个元素,命令为 ZPOPMIN game:ranking:daily 50;
  3. 库存兜底处理:弹出分数最低的用户(最后兜底的库存),命令为 ZPOPMIN seckill:reserve 1;
  4. 低分数据清理:删除热度最低的内容(用于释放内存),命令为 ZPOPMIN content:ranking:hot 100。

2.10 BZPOPMIN:ZPOPMIN 的阻塞版本

2.10.1 核心作用(详细版)

BZPOPMIN 是 ZPOPMIN 的阻塞增强版本,核心作用是原子性地弹出指定有序集合中分数最低的元素;若所有指定的 ZSet 均为空,客户端会阻塞等待,直到有元素被添加到任意一个 ZSet 中,或等待时间达到超时阈值。

该命令是实现 "分布式阻塞低优先级队列、低分元素实时处理" 的核心,特性与 BZPOPMAX 完全一致,仅弹出的是分数最低的元素。

2.10.2 完整语法
复制代码
BZPOPMIN key [key ...] timeout
  • key [key ...]:必填,一个或多个目标有序集合的键名(按顺序优先级监听);
  • timeout:必填,阻塞超时时间(单位:秒),0 代表永久阻塞。
2.10.3 逐行实操示例
复制代码
# 1. 清空待监听的 ZSet
redis> DEL zset1 zset2
(integer) 0

# 2. 向 zset1 添加 3 个元素(分数 0、1、2)
redis> ZADD zset1 0 "a" 1 "b" 2 "c"
(integer) 3

# 3. 阻塞弹出分数最低的元素(timeout=0 永久阻塞)
redis> BZPOPMIN zset1 zset2 0
1) "zset1"   # 弹出元素所属的 ZSet 键名
2) "a"       # 被弹出的元素名称
3) "0"       # 被弹出元素的分数

# 补充示例:监听空 ZSet,超时 5 秒
redis> BZPOPMIN zset1 zset2 5
(nil) # 超时返回 nil
2.10.4 返回值解析

BZPOPMIN 的返回值规则与 BZPOPMAX 完全一致:

  1. 成功弹出:返回「所属 ZSet 键名、元素名称、元素分数」的一维数组;
  2. 超时无元素:返回 nil。
2.10.5 关键细节说明
  • 监听优先级:按 key 参数顺序监听,优先弹出第一个非空 ZSet 的分数最低元素;
  • 原子性:弹出操作原子化,多客户端无竞态条件;
  • 阻塞特性:阻塞期间不占用 Redis 主线程资源;
  • 空键处理:不存在的 key 视为空 ZSet。
2.10.6 实战业务场景(详细版)
  1. 分布式低优先级任务队列:多服务器监听低优先级任务队列,有任务时原子性获取并执行,命令为 BZPOPMIN task:queue:low 0;
  2. 低分数据实时处理:监听低分内容队列,有新内容时立即处理,命令为 BZPOPMIN content:queue:low 0;
  3. 兜底任务调度:监听 "兜底任务队列",无任务时阻塞等待,命令为 BZPOPMIN task:queue:backup 60。

2.11 ZRANK:查询元素升序排名(分数小 → 大)

2.11.1 核心作用(详细版)

ZRANK 是有序集合的 "排名查询" 核心命令,核心作用是查询指定元素在有序集合中的升序排名(分数从小到大),排名从 0 开始计数 ;若元素不存在于 ZSet 中,返回 nil。

该命令是实现 "元素排名查询、分数位次确认" 的基础,与 ZREVRANK 是互补关系,前者查升序排名(低分位次),后者查降序排名(高分位次)。

2.11.2 完整语法
复制代码
ZRANK key member
  • key:必填,目标有序集合的键名;
  • member:必填,需要查询排名的元素名称(字符串类型)。
2.11.3 逐行实操示例
复制代码
# 1. 向 myzset 添加 3 个元素(分数 1、2、3)
redis> ZADD myzset 1 "one" 2 "two" 3 "three"
(integer) 3

# 2. 查询 "three" 的升序排名(分数 3,升序第 2 位,0 开始)
redis> ZRANK myzset "three"
(integer) 2

# 3. 查询 "two" 的升序排名(分数 2,升序第 1 位)
redis> ZRANK myzset "two"
(integer) 1

# 4. 查询不存在的元素 "four"(返回 nil)
redis> ZRANK myzset "four"
(nil)

# 5. 查询 "one" 的升序排名(分数 1,升序第 0 位)
redis> ZRANK myzset "one"
(integer) 0
2.11.4 返回值解析

ZRANK 的返回值分两种情况:

  1. 元素存在:返回整数类型的升序排名(从 0 开始);
  2. 元素不存在(或目标 key 非 ZSet 类型):返回 nil(空)。
2.11.5 关键细节说明
  • 排名规则:升序排名从 0 开始,分数最小的元素排名为 0;
  • 分数相同:若多个元素分数相同,排名按元素名称的字典序(lexicographical order)排序;
  • 性能特性:时间复杂度 O(log(N)),查询效率极高(与 ZSet 元素总数无关);
  • 空键处理:目标 key 不存在时,返回 nil,无报错。
2.11.6 实战业务场景(详细版)
  1. 学生成绩升序排名:查询学生 "张三" 的数学成绩升序排名,命令为 ZRANK class:1:math:score "张三";
  2. 积分位次查询:查询用户 "user1001" 的积分升序排名(低分位次),命令为 ZRANK user:ranking:points "user1001";
  3. 战力位次确认:查询武将 "赵云" 的武力值升序排名,命令为 ZRANK game:general:power "赵云";
  4. 内容热度位次:查询文章 "art10086" 的热度升序排名,命令为 ZRANK content:ranking:hot "art10086"。

2.12 ZREVRANK:查询元素降序排名(分数大 → 小)

2.12.1 核心作用(详细版)

ZREVRANK 是 ZRANK 的逆序版本,核心作用是查询指定元素在有序集合中的降序排名(分数从大到小),排名从 0 开始计数(0 代表第一名) ;若元素不存在于 ZSet 中,返回 nil。

该命令是业务中 "展示用户排名、元素位次" 最常用的命令,因为用户更关注 "第几名"(降序),而非升序位次。

2.12.2 完整语法
复制代码
ZREVRANK key member
  • key:必填,目标有序集合的键名;
  • member:必填,需要查询排名的元素名称。
2.12.3 逐行实操示例
复制代码
# 1. 向 myzset 添加 3 个元素(分数 1、2、3)
redis> ZADD myzset 1 "one" 2 "two" 3 "three"
(integer) 3

# 2. 查询 "one" 的降序排名(分数 1,降序第 2 位)
redis> ZREVRANK myzset "one"
(integer) 2

# 3. 查询 "three" 的降序排名(分数 3,降序第 0 位,第一名)
redis> ZREVRANK myzset "three"
(integer) 0

# 4. 查询 "two" 的降序排名(分数 2,降序第 1 位)
redis> ZREVRANK myzset "two"
(integer) 1

# 5. 查询不存在的元素 "four"(返回 nil)
redis> ZREVRANK myzset "four"
(nil)
2.12.4 返回值解析

ZREVRANK 的返回值规则与 ZRANK 完全一致:

  1. 元素存在:返回整数类型的降序排名(从 0 开始);
  2. 元素不存在(或目标 key 非 ZSet 类型):返回 nil。
2.12.5 关键细节说明
  • 排名规则:降序排名从 0 开始,分数最大的元素排名为 0(第一名);
  • 分数相同:元素分数相同时,排名按元素名称的字典序逆序排列;
  • 性能特性:时间复杂度 O(log(N)),查询效率极高;
  • 空键处理:目标 key 不存在时,返回 nil,无报错。
2.12.6 实战业务场景(详细版)
  1. 全国用户排名展示:查询用户 "user8888" 的全国积分降序排名,命令为 ZREVRANK user:ranking:national "user8888",返回 0 则代表全国第一;
  2. 班级成绩排名:查询学生 "李四" 的班级数学成绩降序排名,命令为 ZREVRANK class:1:math:score "李四";
  3. 游戏武将排名:查询武将 "关羽" 的全服武力值降序排名,命令为 ZREVRANK game:general:power "关羽";
  4. 电商商品销量排名:查询商品 "prod1001" 的月度销量降序排名,命令为 ZREVRANK shop:ranking:sales:202602 "prod1001";
  5. 内容热度排名:查询文章 "art9999" 的平台热度降序排名,命令为 ZREVRANK content:ranking:hot "art9999"。

2.13 ZSCORE:查询元素对应的分数

2.13.1 核心作用(详细版)

ZSCORE 是有序集合的 "分数查询" 核心命令,核心作用是精准查询指定元素在有序集合中绑定的分数值,时间复杂度为 O (1),是所有 ZSet 命令中性能最优的命令之一。

该命令的核心优势是 "极致性能":无论 ZSet 中有多少元素,查询单个元素的分数都能瞬间完成;若元素不存在于 ZSet 中,返回 nil。

2.13.2 完整语法
复制代码
ZSCORE key member
  • key:必填,目标有序集合的键名;
  • member:必填,需要查询分数的元素名称。
2.13.3 逐行实操示例
复制代码
# 1. 向 myzset 添加元素 "one",分数为 1
redis> ZADD myzset 1 "one"
(integer) 1  # 返回值:成功添加的元素个数

# 2. 查询 "one" 的分数(返回字符串格式的 "1")
redis> ZSCORE myzset "one"
"1"

# 3. 查询不存在的元素 "two"(返回 nil)
redis> ZSCORE myzset "two"
(nil)

# 补充示例:查询浮点数分数的元素
redis> ZADD myzset 95.5 "zhangsan"
(integer) 1
redis> ZSCORE myzset "zhangsan"
"95.5"
2.13.4 返回值解析

ZSCORE 的返回值分两种情况:

  1. 元素存在:返回字符串格式的分数值(整数 / 浮点数均以字符串存储);
  2. 元素不存在(或目标 key 非 ZSet 类型 /key 不存在):返回 nil。
2.13.5 关键细节说明
  • 性能特性:时间复杂度 O(1),极致性能,适合高并发查询;
  • 数据类型:返回的分数为字符串类型,客户端需自行转换为数值类型(如 int/float);
  • 空键处理:目标 key 不存在时,返回 nil,无报错;
  • 精度问题:若分数为浮点数,Redis 会保留原始精度,无精度丢失。
2.13.6 实战业务场景(详细版)
  1. 用户积分查询:查询用户 "user1001" 的当前积分,命令为 ZSCORE user:ranking:points "user1001";
  2. 内容点赞数查询:查询文章 "art10086" 的当前点赞数,命令为 ZSCORE article:like:10086 "total";
  3. 武将武力值查询:查询武将 "吕布" 的武力值,命令为 ZSCORE game:general:power "吕布";
  4. 学生成绩查询:查询学生 "王五" 的数学成绩,命令为 ZSCORE class:1:math:score "王五";
  5. 商品销量查询:查询商品 "prod2002" 的今日销量,命令为 ZSCORE shop:sales:today "prod2002";
  6. 视频播放量查询:查询视频 "vid5253" 的总播放量,命令为 ZSCORE video:play:total "vid5253"。

2.14 ZREM:删除指定元素

2.14.1 核心作用(详细版)

ZREM 是有序集合的 "元素删除" 核心命令,核心作用是从有序集合中删除一个或多个指定的元素,返回实际被删除的元素个数;若指定的元素不存在于 ZSet 中,会自动忽略,不会报错。

该命令是实现 "元素精准删除、违规内容移除、账号注销清理" 等业务的核心,支持批量删除多个元素,且操作是原子性的。

2.14.2 完整语法
复制代码
ZREM key member [member ...]
  • key:必填,目标有序集合的键名;
  • member [member ...]:必填,一个或多个需要删除的元素名称(批量删除用空格分隔)。
2.14.3 逐行实操示例
复制代码
# 1. 向 myzset 添加 3 个元素(分数 1、2、3)
redis> ZADD myzset 1 "one" 2 "two" 3 "three"
(integer) 3

# 2. 删除元素 "two"(返回实际删除个数 1)
redis> ZREM myzset "two"
(integer) 1

# 3. 查看删除后的 ZSet(仅剩 one、three)
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "one"
2) "1"
3) "three"
4) "3"

# 4. 批量删除多个元素(删除 one、three、four)
redis> ZREM myzset "one" "three" "four"
(integer) 2  # 仅删除了 one、three,four 不存在,忽略

# 5. 查看最终 ZSet(为空)
redis> ZRANGE myzset 0 -1 WITHSCORES
(empty array)
2.14.4 返回值解析

ZREM 的返回值为整数类型,代表实际被删除的元素个数(仅包含存在于 ZSet 中的元素);若目标 key 不存在,或所有指定元素均不存在,返回 0。

2.14.5 关键细节说明
  • 原子性:批量删除多个元素时,操作是原子的,要么全部删除(存在的元素),要么都不删除;
  • 性能特性:时间复杂度 O(M*log(N)),M 为指定的元素个数,N 为 ZSet 总元素数;批量删除少量元素时性能极高;
  • 空键处理:目标 key 不存在时,返回 0,无报错;
  • 不存在元素处理:指定的元素不存在时,自动忽略,不影响其他元素的删除。
2.14.6 实战业务场景(详细版)
  1. 作弊用户移除:从排行榜中删除作弊用户 "user9999",命令为 ZREM game:ranking:power "user9999";
  2. 违规内容删除:从热度榜单中删除违规文章 "art8888",命令为 ZREM content:ranking:hot "art8888";
  3. 注销账号清理:删除注销用户的所有相关排名(积分、战力),命令为 ZREM user:ranking:points "user7777" ZREM game:ranking:power "user7777";
  4. 批量清理失效元素:删除榜单中失效的 10 个商品,命令为 ZREM shop:ranking:sales:202602 "prod1001" "prod1002" ... "prod1010";
  5. 抽奖资格取消:取消用户 "user5555" 的抽奖资格,命令为 ZREM lottery:queue "user5555"。

2.15 ZREMRANGEBYRANK:按升序排名区间删除元素

2.15.1 核心作用(详细版)

ZREMRANGEBYRANK 是有序集合的 "区间删除" 命令,核心作用是按照元素的升序排名区间(下标区间)删除元素,区间为左闭右闭;返回实际被删除的元素个数,是实现 "榜单末尾元素清理、保留 TOP N" 类业务的核心命令。

该命令的关键是:删除的是 "升序排名" 的下标区间(0 为分数最小的元素,-1 为分数最大的元素),而非降序,需注意与业务需求匹配。

2.15.2 完整语法
复制代码
ZREMRANGEBYRANK key start stop
  • key:必填,目标有序集合的键名;
  • start:必填,升序排名的起始下标(整数,支持负数,0 为分数最小的元素);
  • stop:必填,升序排名的结束下标(整数,支持负数,-1 为分数最大的元素)。
2.15.3 逐行实操示例
复制代码
# 1. 向 myzset 添加 3 个元素(分数 1、2、3)
redis> ZADD myzset 1 "one" 2 "two" 3 "three"
(integer) 3

# 2. 删除升序排名 0~1 的元素(one、two,分数最小的两个)
redis> ZREMRANGEBYRANK myzset 0 1
(integer) 2  # 返回实际删除的元素个数

# 3. 查看剩余元素(仅 three,分数最大)
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "three"
2) "3"

# 补充示例:保留 TOP100,删除排名 100 以外的所有元素
# redis> ZREMRANGEBYRANK user:ranking 100 -1
# (integer) N  # N 为被删除的元素个数
2.15.4 返回值解析

ZREMRANGEBYRANK 的返回值为整数类型,代表实际被删除的元素个数;若目标 key 不存在,或区间内无元素,返回 0。

2.15.5 关键细节说明
  • 排名规则:删除的是升序排名的下标区间(0 = 分数最小,-1 = 分数最大);若需删除降序排名的末尾元素,需转换为升序下标;
  • 区间容错:start/stop 超出 ZSet 实际下标范围时,仅删除存在的元素,无报错;
  • 性能特性:时间复杂度 O(log(N)+M),M 为被删除的元素数;删除大量元素时需注意耗时;
  • 空键处理:目标 key 不存在时,返回 0,无报错。
2.15.6 实战业务场景(详细版)
  1. 榜单保留 TOP100:仅保留用户积分排行榜的前 100 名,删除排名 100 以外的所有元素,命令为 ZREMRANGEBYRANK user:ranking:points 100 -1;
  2. 清理低分武将:删除武力值升序排名 0~99 的武将(分数最低的 100 个),命令为 ZREMRANGEBYRANK game:general:power 0 99;
  3. 销量榜单裁剪:仅保留月度销量 TOP500 的商品,删除排名 500 以外的元素,命令为 ZREMRANGEBYRANK shop:ranking:sales:202602 500 -1;
  4. 热度榜单瘦身:删除热度升序排名 0~499 的内容(低分内容),命令为 ZREMRANGEBYRANK content:ranking:hot 0 499。

2.16 ZREMRANGEBYSCORE:按分数区间删除元素

2.16.1 核心作用(详细版)

ZREMRANGEBYSCORE 是有序集合的 "分数区间删除" 核心命令,核心作用是按照指定的分数区间删除有序集合中的元素,支持闭区间(默认)、开区间(使用 ( 标识);返回实际被删除的元素个数,是实现 "低分元素清理、条件删除" 类业务的核心命令。

2.16.2 完整语法
复制代码
ZREMRANGEBYSCORE key min max
  • key:必填,目标有序集合的键名;
  • min:必填,分数区间下限,支持 -inf、(数值(开区间);
  • max:必填,分数区间上限,支持 +inf、(数值(开区间)。
2.16.3 逐行实操示例
复制代码
# 1. 向 myzset 添加 3 个元素(分数 1、2、3)
redis> ZADD myzset 1 "one" 2 "two" 3 "three"
(integer) 3

# 2. 删除分数 < 2 的元素(-inf 到 (2)
redis> ZREMRANGEBYSCORE myzset -inf (2
(integer) 1  # 仅删除了 one(分数 1)

# 3. 查看剩余元素(two、three)
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "two"
2) "2"
3) "three"
4) "3"

# 补充示例:删除分数 ≥3 的元素(3 到 +inf)
redis> ZREMRANGEBYSCORE myzset 3 +inf
(integer) 1  # 删除了 three

# 补充示例:删除分数 2~3 的元素(闭区间)
redis> ZREMRANGEBYSCORE myzset 2 3
(integer) 1  # 删除了 two(仅剩空)
2.16.4 返回值解析

ZREMRANGEBYSCORE 的返回值为整数类型,代表实际被删除的元素个数;若目标 key 不存在,或区间内无元素,返回 0。

2.16.5 关键细节说明
  • 区间规则:默认闭区间,(数值 代表开区间(不包含该数值);
  • 无限区间:-inf/+inf 代表负无穷 / 正无穷,无需加引号;
  • 性能特性:时间复杂度 O(log(N)+M),M 为被删除的元素数;删除大量元素时需注意耗时;
  • 空键处理:目标 key 不存在时,返回 0,无报错;
  • 原子性:删除操作是原子的,批量删除区间内元素时,要么全部删除,要么都不删除。
2.16.6 实战业务场景(详细版)
  1. 不及格学生清理:从成绩榜单中删除分数 < 60 分的学生,命令为 ZREMRANGEBYSCORE class:1:math:score -inf (60;
  2. 低热度内容清理:删除热度 < 10 的低质内容,命令为 ZREMRANGEBYSCORE content:ranking:hot -inf (10;
  3. 低武力值武将删除:删除武力值 < 90 的武将,命令为 ZREMRANGEBYSCORE game:general:power -inf (90;
  4. 低积分用户淘汰:删除积分 < 1000 的普通用户,命令为 ZREMRANGEBYSCORE user:ranking:points -inf (1000;
  5. 高分数元素清理:删除分数 > 10000 的异常元素(作弊用户),命令为 ZREMRANGEBYSCORE game:ranking:power (10000 +inf。

2.17 ZINCRBY:元素分数增量修改(原子性)

2.17.1 核心作用(详细版)

ZINCRBY 是有序集合的 "分数增量修改" 核心命令,核心作用是原子性地对指定元素的分数进行增量调整(可加可减);若元素不存在于 ZSet 中,会自动初始化该元素的分数为 0,再执行增量操作;返回修改后的最新分数。

该命令的 "原子性" 是核心特性:即使高并发场景下(如百万级并发点赞),也不会出现分数计算错误的问题,是实现 "实时计分、增量更新" 类业务的唯一选择。

2.17.2 完整语法
复制代码
ZINCRBY key increment member
  • key:必填,目标有序集合的键名;
  • increment:必填,分数增量(可正可负,支持整数 / 浮点数);正数为加分,负数为减分;
  • member:必填,需要修改分数的元素名称。
2.17.3 有效版本

1.2.0 及以上(全版本兼容,无兼容性风险)

2.17.4 逐行实操示例
复制代码
# 1. 向 myzset 添加 2 个元素(分数 1、2)
redis> ZADD myzset 1 "one" 2 "two"
(integer) 2

# 2. 给 "one" 的分数 +2(1+2=3),返回最新分数
redis> ZINCRBY myzset 2 "one"
"3"

# 3. 查看修改后的 ZSet(one=3,two=2)
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "two"
2) "2"
3) "one"
4) "3"

# 4. 给不存在的元素 "three" 加分(初始化 0+5=5)
redis> ZINCRBY myzset 5 "three"
"5"

# 5. 查看新增后的 ZSet(three=5)
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "two"
2) "2"
3) "one"
4) "3"
5) "three"
6) "5"

# 6. 给 "one" 的分数减 1(3-1=2)
redis> ZINCRBY myzset -1 "one"
"2"
2.17.5 返回值解析

ZINCRBY 的返回值为字符串格式的最新分数(整数 / 浮点数均以字符串存储);无论元素是否存在,都会返回修改后的分数(不存在则为增量值)。

2.17.6 关键细节说明
  • 原子性:增量操作是原子的,高并发下无竞态条件(如 1000 个客户端同时给同一个元素 + 1,最终分数会准确 + 1000);
  • 初始化规则:元素不存在时,自动创建并初始化分数为 0,再执行增量;
  • 数据类型:返回的分数为字符串类型,客户端需自行转换为数值类型;
  • 精度问题:浮点数增量(如 0.5)不会丢失精度,Redis 会保留原始值;
  • 性能特性:时间复杂度 O(log(N)),极致性能,适合高并发增量更新。
2.17.7 实战业务场景
  1. 文章点赞 + 1:给文章 "art1001" 的点赞数 + 1,命令为 ZINCRBY article:like:1001 1 "total";
  2. 取消点赞 - 1:取消文章 "art1001" 的点赞,命令为 ZINCRBY article:like:1001 -1 "total";
  3. 视频播放量 + 1:给视频 "vid5253" 的播放量 + 1,命令为 ZINCRBY video:play:5253 1 "total";
  4. 用户积分增加:给用户 "user1001" 的积分 + 10,命令为 ZINCRBY user:ranking:points 10 "user1001";
  5. 武将武力值提升:给武将 "关羽" 的武力值 + 0.5,命令为 ZINCRBY game:general:power 0.5 "关羽";

三、ZSet 集合间高级操作(交集 ZINTERSTORE、并集 ZUNIONSTORE)

ZSet 最强大的高级功能是多集合聚合运算(交集、并集),支持权重(WEIGHTS)+ 聚合方式(AGGREGATE),完美实现「多榜单合并、综合排名、加权计分」,是电商、游戏、内容平台的核心高级用法。

3.1 集合操作核心概念

  • 交集(ZINTERSTORE) :只保留所有输入 ZSet 都共同存在的元素,不同集合中同一个元素的分数按指定规则合并计算,最终存入新的 ZSet;不存在于所有集合的元素会被直接丢弃。
  • 并集(ZUNIONSTORE):合并所有输入 ZSet 的全部元素(自动去重,相同元素只保留一份),相同元素在不同集合中的分数按规则合并,最终存入新 ZSet。
  • WEIGHTS 权重 :对每个输入 ZSet 设置独立权重系数,执行聚合前,该集合内所有元素的分数会先乘以对应权重,再参与 SUM/MIN/MAX 计算,用于实现「不同榜单权重不同」的综合计分。
  • AGGREGATE 聚合规则 :相同元素在多个集合中的分数合并方式:
    • SUM(默认):所有集合分数(乘权重后)相加;
    • MIN:取所有集合中分数(乘权重后)的最小值;
    • MAX:取所有集合中分数(乘权重后)的最大值。

3.2 ZINTERSTORE:多 ZSet 交集聚合(保存到新 ZSet)

3.2.1 核心作用

ZINTERSTORE 用于计算多个有序集合的交集,只保留同时存在于所有输入 ZSet 中的共同元素;每个集合分数先按 WEIGHTS 加权,再按 AGGREGATE 规则合并为新分数;最终把结果写入目标 ZSet,并返回新集合的元素个数。

该命令是多维度榜单筛选、共同元素统计、加权综合评分的核心命令,常用于 "同时上榜多个榜单" 的优质内容 / 高价值用户筛选。

3.2.2 完整语法

复制代码
ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]

3.2.3 关键参数解释

  • destination:必填,结果保存的目标新 ZSet 键名(若已存在会被覆盖);
  • numkeys:必填且必须正确 ,代表后面跟着的输入 ZSet 个数,不能多不能少,是 Redis 语法强制要求;
  • key [key ...]:必填,参与交集计算的原始 ZSet 键列表;
  • WEIGHTS weight [weight ...]:可选,权重列表,个数必须与输入 ZSet 数量一致,默认所有权重 = 1;
  • AGGREGATE SUM|MIN|MAX:可选,分数合并规则,默认 SUM。

3.2.4 逐行实操示例(完整原文 + 注释)

复制代码
# 1. 创建 zset1:one=1、two=2
redis> ZADD zset1 1 "one" 2 "two"
(integer) 2

# 2. 创建 zset2:one=1、two=2、three=3
redis> ZADD zset2 1 "one" 2 "two" 3 "three"
(integer) 3

# 3. 交集聚合:2 个 ZSet,zset1 权重 2,zset2 权重 3,聚合规则 SUM
redis> ZINTERSTORE out 2 zset1 zset2 WEIGHTS 2 3
(integer) 2

# 4. 计算规则(逐元素清晰说明)
# one = 1*2 + 1*3 = 2 + 3 = 5
# two = 2*2 + 2*3 = 4 + 6 = 10
# three 只在 zset2 存在,不属于交集,被丢弃
redis> ZRANGE out 0 -1 WITHSCORES
1) "one"
2) "5"
3) "two"
4) "10"

3.2.5 返回值解析

返回整数 ,代表交集计算后,目标 ZSet destination 中最终保存的元素数量;

  • 若所有集合无共同元素,返回 0;
  • 若输入 key 不存在,视为空集合,交集结果也为空,返回 0。

3.2.6 关键细节说明

  1. 覆盖规则:目标 key 若已存在且非 ZSet 类型,会被覆盖为 ZSet;
  2. 权重顺序:WEIGHTS 顺序必须与 key 顺序严格一一对应;
  3. 空集合处理:任意一个输入 ZSet 为空,则最终交集一定为空;
  4. 性能:时间复杂度 O (N),N 为所有集合元素总数,集合极大时建议低峰执行。

3.2.7 实战业务场景(完整版)

  1. 爆款内容筛选:同时出现在日榜、周榜、月榜 的内容,取综合加权得分,命令:ZINTERSTORE content:hot:all 3 content:hot:day content:hot:week content:hot:month WEIGHTS 1 2 3 AGGREGATE SUM
  2. 跨服共同高分玩家:同时进入多个区服排行榜的玩家,取最低分为最终判定分:ZINTERSTORE user:common:ranking 2 user:ranking:server1 user:ranking:server2 AGGREGATE MIN
  3. 多维度达标筛选:同时满足 "积分≥1000、战力≥500、等级≥60" 的用户,交集筛选。

3.3 ZUNIONSTORE:多 ZSet 并集聚合(保存到新 ZSet)

3.3.1 核心作用

ZUNIONSTORE 用于计算多个有序集合的并集,合并所有集合的全部元素(相同元素自动去重);每个集合分数先按 WEIGHTS 加权,再按 AGGREGATE 合并为新分数;结果写入目标 ZSet,并返回新集合元素个数。

它是全服榜单合并、多维度积分汇总、综合热度计算的核心命令,几乎所有大型排行榜底层都会使用。

3.3.2 完整语法

复制代码
ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]

3.3.3 关键参数解释(与 ZINTERSTORE 完全一致)

  • destination:结果保存的新 ZSet;
  • numkeys:输入 ZSet 个数(必填);
  • key [...]:参与并集的原始 ZSet;
  • WEIGHTS:权重列表;
  • AGGREGATE:SUM/MIN/MAX,默认 SUM。

3.3.4 逐行实操示例(原文完整保留)

复制代码
# 1. 创建 zset1:one=1、two=2
redis> ZADD zset1 1 "one" 2 "two"
(integer) 2

# 2. 创建 zset2:one=1、two=2、three=3
redis> ZADD zset2 1 "one" 2 "two" 3 "three"
(integer) 3

# 3. 并集聚合:2 个 ZSet,权重 2、3,聚合 SUM
redis> ZUNIONSTORE out 2 zset1 zset2 WEIGHTS 2 3
(integer) 3

# 4. 结果计算逻辑
# one = 1*2 + 1*3 = 5
# two = 2*2 + 2*3 = 10
# three = 0*2 + 3*3 = 9(只在 zset2 存在,zset1 视为 0)
redis> ZRANGE out 0 -1 WITHSCORES
1) "one"
2) "5"
3) "three"
4) "9"
5) "two"
6) "10"

3.3.5 返回值解析

返回整数,代表并集去重后,目标 ZSet 中最终的元素总数;

  • 即使所有输入集合都为空,也返回 0;
  • 元素去重只按 member 字符串匹配,与分数无关。

3.3.6 关键细节说明

  1. 去重规则:member 字符串完全相同才算同一个元素,大小写、空格不同视为不同元素;
  2. 单集合不存在:不存在的 ZSet 视为空集合,不影响并集结果(只合并存在的集合);
  3. 权重为 0:可通过权重 0 让某个集合只参与 "存在性判断",分数不参与计算;
  4. 大数据量注意:并集会加载所有集合元素,超大 ZSet 执行时可能占用较多 CPU。

3.3.7 实战业务场景

  1. 全服总排行榜 :合并安卓区、iOS 区、渠道区榜单,加权求和:ZUNIONSTORE user:ranking:total 3 user:ranking:android user:ranking:ios user:ranking:channel WEIGHTS 1 1 1 AGGREGATE SUM
  2. 内容综合热度 :热度 = 点赞 0.4 + 评论 0.3 + 收藏 0.3,用并集加权实现:ZUNIONSTORE content:hot:total 3 content:like content:comment content:star WEIGHTS 0.4 0.3 0.3 AGGREGATE SUM
  3. 用户总积分汇总:合并每日任务积分、成就积分、活动积分,取总和作为总排名依据

四、ZSet 全命令汇总与速查表

为方便日常开发、面试复习,我们将所有 ZSet 命令按功能、语法、时间复杂度、核心作用整理成终极速查表,覆盖 100% 场景:

命令分类 命令 核心语法 时间复杂度 核心作用
基础写入 ZADD ZADD key NX/XX/GT/LT/CH/INCR score member O(k·log(N)) 添加 / 更新元素 + 分数
计数查询 ZCARD ZCARD key O(1) 查询元素总个数
区间计数 ZCOUNT ZCOUNT key min max O(log(N)) 统计分数区间元素个数
下标查询(升) ZRANGE ZRANGE key start stop WITHSCORES O(log(N)+M) 按下标升序查询
下标查询(降) ZREVRANGE ZREVRANGE key start stop WITHSCORES O(log(N)+M) 按下标降序查询(TOP N 核心)
分数查询(升) ZRANGEBYSCORE ZRANGEBYSCORE key min max WITHSCORES O(log(N)+M) 按分数升序查询
弹出最大分数 ZPOPMAX ZPOPMAX key count O(log(N)·M) 删除 + 返回分数最高元素
阻塞弹出最大 BZPOPMAX BZPOPMAX key key... timeout O(log(N)) 阻塞式弹出最大分数
弹出最小分数 ZPOPMIN ZPOPMIN key count O(log(N)·M) 删除 + 返回分数最低元素
阻塞弹出最小 BZPOPMIN BZPOPMIN key key... timeout O(log(N)) 阻塞式弹出最小分数
升序排名 ZRANK ZRANK key member O(log(N)) 查询元素升序排名(0 开始)
降序排名 ZREVRANK ZREVRANK key member O(log(N)) 查询元素降序排名(TOP 排名)
分数查询 ZSCORE ZSCORE key member O(1) 查询元素绑定分数
删除指定元素 ZREM ZREM key member member... O(k·log(N)) 删除指定元素
按排名删除 ZREMRANGEBYRANK ZREMRANGEBYRANK key start stop O(log(N)+M) 按升序排名区间删除
按分数删除 ZREMRANGEBYSCORE ZREMRANGEBYSCORE key min max O(log(N)+M) 按分数区间删除
增量计分 ZINCRBY ZINCRBY key increment member O(log(N)) 原子性增量修改分数(高并发核心)
交集聚合 ZINTERSTORE ZINTERSTORE dest numkeys key... WEIGHTS... AGGREGATE... O(N·K + M·log(M)) 多 ZSet 交集聚合
并集聚合 ZUNIONSTORE ZUNIONSTORE dest numkeys key... WEIGHTS... AGGREGATE... O(N + M·log(M)) 多 ZSet 并集聚合

五、ZSet 内部编码(底层实现:ziplist 压缩列表、skiplist 跳表)

Redis 所有数据类型都遵循「对外统一接口,对内智能编码 」的设计思想,ZSet 也不例外 ------ 底层有两种编码,Redis 会根据元素个数、元素大小自动切换,对开发者完全透明,理解编码能帮助我们优化内存、提升性能。

5.1 ZSet 两种内部编码

ZSet 只有两种内部编码:

  1. ziplist(压缩列表):小数据量、小元素时使用,极致节省内存;
  2. skiplist(跳表):大数据量、大元素时使用,保证高性能操作。

5.2 ziplist 编码:触发条件、结构、优缺点

5.2.1 触发条件

  • 元素个数 < zset-max-ziplist-entries(默认 128 个);
  • 每个元素的长度 < zset-max-ziplist-value(默认 64 字节)。

5.2.2 存储结构

连续内存块,按「元素 1 + 分数 1 + 元素 2 + 分数 2...」顺序存储,无指针、无冗余,内存占用极低。

5.2.3 优点

  • 内存利用率极高,无碎片;
  • 小数据量下操作速度快。

5.2.4 缺点

  • 元素多 / 大后,插入 / 删除需要批量移动内存,性能急剧下降;
  • 不支持高效的范围查询、排名计算。

5.2.5 实操查看编码

复制代码
# 1. 添加 3 个小元素
127.0.0.1:6379> zadd zsetkey 50 e1 60 e2 30 e3
(integer) 3

# 2. 查看编码:ziplist
127.0.0.1:6379> object encoding zsetkey
"ziplist"

5.3 skiplist 编码:触发条件、结构、优缺点

5.3.1 触发条件(任意一条满足,教材原文)

  • 元素个数 ≥ 128 个;
  • 任意元素长度 ≥ 64 字节。

5.3.2 存储结构

跳表(skiplist)+ 哈希表(dict):

  • 跳表:实现排序、范围查询、排名(O(logN));
  • 哈希表:实现元素→分数的快速查找(O(1))。

5.3.3 优点

  • 插入 / 删除 / 查询 / 排名均为 O (logN),海量数据下性能极致;
  • 支持高效范围查询、排名计算、动态排序。

5.3.4 缺点

  • 内存占用比 ziplist 高(有指针、多层索引);
  • 结构更复杂。

5.3.5 实操查看编码

复制代码
# 1. 添加超过 64 字节的大元素
127.0.0.1:6379> zadd zsetkey 50 "一个超长字符串超过64字节.................................."
(integer) 1

# 2. 查看编码:skiplist
127.0.0.1:6379> object encoding zsetkey
"skiplist"

5.4 编码自动转换规则(单向不可逆,重点)

Redis 编码转换是单向、不可逆的:

  • 满足 ziplist 条件 → 使用 ziplist;
  • 不满足 ziplist 条件 → 自动切换为 skiplist;
  • skiplist 永远不会转回 ziplist(即使后续删除元素、缩短长度)。

5.5 编码设计的核心价值

  • 小数据量用 ziplist:省内存;
  • 大数据量用 skiplist:保性能;
  • 自动切换:开发者无需手动配置,降低使用成本。

六、ZSet 核心实战场景

ZSet 的实战场景覆盖互联网 90% 的「排序、排名、计分、队列」需求,其中排行榜系统是教材指定的最核心场景,我们完整落地,同时扩展高频高级场景。

6.1 最核心场景:排行榜系统

6.1.1 需求

  • 维护每日文章点赞排行榜;
  • 支持点赞 + 1、取消点赞 - 1;
  • 展示 TOP10;
  • 查询用户排名、分数;
  • 删除违规 / 注销用户。

6.1.2 键名设计(规范)

user:ranking:日期 → 如 user:ranking:20260208

6.1.3 完整命令落地(教材步骤)

步骤 1:初始化 / 添加用户赞数
复制代码
# 用户 james 初始 3 个赞
ZADD user:ranking:20260208 3 james
步骤 2:点赞 +1(增量修改,原子性)
复制代码
ZINCRBY user:ranking:20260208 1 james
步骤 3:取消点赞 -1(负增量)
复制代码
ZINCRBY user:ranking:20260208 -1 james
步骤 4:删除违规用户(如 tom)
复制代码
ZREM user:ranking:20260208 tom
步骤 5:展示 TOP10(降序,分数从高到低)
复制代码
ZREVRANGE user:ranking:20260208 0 9 WITHSCORES
步骤 6:查询用户分数(点赞数)
复制代码
ZSCORE user:ranking:20260208 mike
步骤 7:查询用户排名(降序,TOP 名次)
复制代码
ZREVRANK user:ranking:20260208 mike
步骤 8:结合 Hash 存储用户详细信息(教材)
复制代码
# Hash 存储用户信息
HSET user:info:tom name "Tom" age 25 sex "男"

# 查询用户信息
HGETALL user:info:tom

6.2 高频扩展实战场景

6.2.1 延时任务队列

  • score = 任务执行时间戳;
  • 用 ZREVRANGE 查询待执行任务,ZREM 标记完成;
  • 适用于订单超时取消、优惠券过期。

6.2.2 优先级任务队列

  • score = 任务优先级(数字越大优先级越高);
  • 用 ZPOPMAX 弹出最高优先级任务执行。

6.2.3 多维度综合排行榜

  • 日榜、周榜、月榜通过 ZUNIONSTORE 加权聚合,生成总榜。

6.2.4 游戏战力 / 段位榜、短视频热度榜、商城销量排序

  • 统一逻辑:score = 战力 / 热度 / 销量,ZREVRANGE 查 TOP,ZINCRBY 实时更新。

七、ZSet 生产环境避坑指南与性能优化

7.1 大 Key 危害:超大 ZSet 阻塞 Redis

  • 禁止 ZRANGE 0 -1 遍历千万级 ZSet(O (N),阻塞单线程);
  • 分页查询用 ZRANGE start stop,每次查询少量元素。

7.2 排名从 0 开始:业务展示需 +1

  • ZREVRANK 返回 0 = 第一名,展示时需 +1,避免用户误解。

7.3 score 浮点数精度丢失

  • 不存储超过 2^53 的超大整数,日常业务分数完全够用。

7.4 禁止 GET+SET 计分:高并发下计数错误

  • 必须用 ZINCRBY/ZADD INCR 原子计分,禁止客户端先读再写。

7.5 集合操作(ZINTERSTORE/ZUNIONSTORE)CPU 消耗高

  • 大集合禁止频繁聚合,提前计算、缓存结果。

7.6 ZSet 无直接过期:榜单自动清理方案

  • 用 ZREMRANGEBYSCORE 清理低分元素;
  • 用定时任务删除旧日期榜单(如 user:ranking:20260201)。

7.7 键名不规范:内存浪费、维护困难

  • 遵循 业务:类型:标识 规范,如 user:ranking:20260208。

7.8 过度依赖分数相同的字典序

  • 业务不可强依赖字典序,分数相同的排序无业务意义。

7.9 阻塞命令超时设置合理

  • BZPOPMAX/BZPOPMIN 不要设置永久阻塞(timeout=0),避免客户端挂起。

7.10 内存优化:尽量适配 ziplist

  • 控制元素个数 < 128,元素长度 < 64 字节,节省内存。

八、ZSet 与其他数据结构选型决策

  • 需要有序 + 去重 + 排名 + 范围查询 → 唯一选择:ZSet;
  • 需要有序 + 重复 → List;
  • 需要去重 + 无序 + 集合运算 → Set;
  • 需要结构化对象 → Hash;
  • 需要简单键值对 → String。

九、全文总结 + 面试高频考点

9.1 全文核心总结

  1. ZSet = 去重 Set + 分数排序,元素唯一、分数可重复、按 score 有序;
  2. 核心命令三巨头:ZADD(写入)、ZINCRBY(增量计分)、ZREVRANGE(TOP N);
  3. 排名命令:ZRANK(升序)、ZREVRANK(降序);
  4. 范围命令:ZRANGE(下标)、ZRANGEBYSCORE(分数);
  5. 删除命令:ZREM、ZREMRANGEBYRANK、ZREMRANGEBYSCORE;
  6. 高级聚合:ZINTERSTORE(交集)、ZUNIONSTORE(并集),支持权重 + 聚合;
  7. 底层编码:ziplist(小数据)、skiplist(大数据),单向不可逆;
  8. 核心场景:排行榜、延时队列、优先级队列、范围筛选;
  9. 高并发核心:原子性计分(ZINCRBY),无竞争、性能极致。

9.2 面试高频 10 大考点

考点 1:ZSet 和 Set、List 的核心区别(必问)

核心区别:

  • List:有序(插入顺序)、可重复、基于下标访问,适合有序序列、消息队列;
  • Set:无序、不可重复、支持集合运算,适合去重、标签、共同好友;
  • ZSet:有序(分数排序)、不可重复、支持排名 / 范围查询,适合排行榜、实时计分。

一句话总结:需要「有序 + 去重 + 排名」选 ZSet,需要「有序 + 重复」选 List,需要「去重 + 无序」选 Set。

考点 2:ZSet 的底层实现(ziplist vs skiplist)

两种编码:

  • ziplist:小数据量(<128 元素,元素 < 64 字节),连续内存、省内存,插入 / 删除慢;
  • skiplist:大数据量,跳表 + 哈希表,插入 / 删除 / 查询 O (logN),性能高、耗内存。

转换规则:单向不可逆,ziplist 可转为 skiplist,反之不行。

考点 3:ZSet 的分数(score)特性

核心特性:

  • 数据类型:64 位浮点数(double),支持整数、小数、±inf;
  • 精度:double 有精度限制,不建议存储超大整数(>2^53);
  • 作用:唯一排序依据,相同分数按字典序排序(不建议强依赖)。

考点 4:ZSet 的有序性如何保证?

有序性保证:

  • 核心依据:分数(score),默认升序,可通过 ZREVRANGE 降序;
  • 分数相同:按元素字典序排序(字母 / 汉字编码顺序);
  • 底层实现:skiplist 结构天然支持有序遍历和范围查询。

考点 5:ZADD 命令的核心选项(NX/XX/GT/LT/CH/INCR)

选项作用:

  • NX/XX:控制新增 / 更新(NX 仅新增,XX 仅更新);
  • GT/LT:控制更新条件(GT 仅新分数 > 旧分数,LT 仅新分数 < 旧分数);
  • CH:修改返回值统计规则(新增 + 更新都计数);
  • INCR:增量计分(等价于 ZINCRBY)。

考点 6:ZINCRBY 的原子性与高并发场景

原子性 :Redis 单线程模型保证 ZINCRBY 是原子操作,高并发下无竞争、无计数错误;使用场景:点赞、播放量、积分等实时计分场景,替代「GET+SET」的非原子操作。

考点 7:ZSet 的集合间操作(ZINTERSTORE/ZUNIONSTORE)

核心作用:

  • ZINTERSTORE:多 ZSet 交集聚合,保留共同元素,分数按权重 + 聚合方式合并;
  • ZUNIONSTORE :多 ZSet 并集聚合,合并所有元素,分数按权重 + 聚合方式合并。使用场景:多榜单合并(日榜 + 周榜 = 总榜)、加权综合评分。

考点 8:ZSet 实现排行榜的核心命令

核心命令组合:

  • 初始化 / 更新:ZADD、ZINCRBY;
  • 查 TOP N:ZREVRANGE(降序);
  • 查排名:ZREVRANK;
  • 查分数:ZSCORE;
  • 删除元素:ZREM、ZREMRANGEBYRANK。

考点 9:ZSet 实现延时任务队列的原理

实现原理:

  • score = 任务执行时间戳;
  • 生产者:ZADD 将任务加入队列;
  • 消费者:定时用 ZRANGEBYSCORE 查询到期任务,ZREM 删除并执行。优势:简单高效,无需额外组件,适合轻量级延时任务。

考点 10:ZSet 的性能优化与常见坑

性能优化:

  • 避免大 Key:拆分超大 ZSet,分页查询;
  • 内存优化:控制元素大小,适配 ziplist 编码;
  • 命令优化:用 ZINCRBY 替代 GET+SET,避免全量遍历。

常见坑:

  • 排名从 0 开始,业务展示需 + 1;
  • 分数精度丢失,避免超大整数;
  • 阻塞命令(BZPOPMAX)不要永久阻塞。

结语

恭喜你,一路学完 String、Hash、List、Set 之后,终于拿下了 Redis 五大基础数据类型中功能最强、设计最精妙、实战与面试双料王者 ------ZSet 有序集合。如果说前面四种结构是 Redis 的「基础工具箱」,那 ZSet 就是真正意义上的「高级引擎」,几乎支撑了互联网中所有与排序、排名、计分、延时、优先级相关的核心业务。

回顾整篇内容,我们从 ZSet 最核心的两条铁律(元素唯一、分数可重复)出发,理清了它与 List、Set 的本质区别;系统性掌握了从 ZADD、ZINCRBY、ZREVRANGE 这类高频基础命令,到 ZINTERSTORE、ZUNIONSTORE 这种多集合加权聚合的高级能力;深入底层理解了 ziplist 与 skiplist 两种编码的自动切换逻辑与性能差异;并落地了排行榜、延时队列、优先级队列、多维度综合榜单等最真实的生产场景,同时梳理了 10 个高频生产坑点与优化方案。整套知识体系,既覆盖了命令使用,又打通了原理、场景、优化、面试全链路。

ZSet 最珍贵的价值,在于它同时拥有 Set 的去重能力、List 的有序特性、哈希表的 O (1) 查找、跳表的 O (logN) 高效排序,再加上原子增量计分、阻塞弹出、集合加权聚合,让它成为轻量级架构下实现复杂业务的「不二之选」。无论是游戏战力排行、短视频热度计算、电商销量榜单、订单超时关闭,还是分布式优先级队列,ZSet 都能用极简的结构、极高的性能轻松承接。

当然,我们也要清醒地记住它的边界:不要把 ZSet 当作超大 Key 随意遍历、不要依赖分数相同时的字典序做强业务逻辑、不要在高并发下用「先查后写」替代 ZINCRBY、不要对超大规模集合频繁做交集并集。用好 ZSet 的关键,是顺应它的设计 ------ 以分数为核心、以排名 / 范围为查询方式、以原子操作为高并发保障、以合理编码控制内存。

至此,Redis 五大基础数据类型已全部学习完毕。从最简单的 String,到结构化 Hash、有序 List、去重 Set,再到全能型 ZSet,你已经拥有了 Redis 最核心的「武器库」。真正的 Redis 高手,从来不是死记命令,而是理解每一种结构的设计初衷、性能特点、适用场景,在合适的业务里选择最合适的工具。

后续我们将进一步深入 Redis 的高级特性:如 HyperLogLog、Geo、Stream、发布订阅、事务、Lua 脚本、持久化、主从、集群、缓存设计 等,一步步从「会用 Redis」走向「精通 Redis」。愿你在实际开发中,真正把 ZSet 用活、用稳、用到极致,让它成为你系统里高性能、高可靠的核心支撑。

我们下篇 Redis 高级内容,继续深入学习~

相关推荐
All for pursuit.1 小时前
【回溯-5】78.子集
数据结构·c++·算法·leetcode
Dxy12393102161 小时前
MySQL 条件注释 vs 普通注释
数据库·mysql
辛苦才能1 小时前
C++ 红黑树(上):五条规则背后的近似平衡与插入的三类情况
开发语言·c++
做运维的阿瑞1 小时前
自连接、交叉连接与复杂 JOIN:一条问题链讲透
前端·数据库·mysql
新鲜势力呀1 小时前
PHP 定时任务系统实战:从 Cron 混乱执行到任务调度中心 + Redis队列 + 失败重试
android·java·redis
by209991 小时前
学会使用 std::string 类,并理解其内部是如何管理字符串的详细阐述(下)
c++·经验分享·笔记·字符串·类和对象·string
.道阻且长.1 小时前
C++11 :新的类功能,lambda,包装器
开发语言·c++·后端
liulilittle1 小时前
短期内不存在通用 AI 工作流魔法
大数据·开发语言·c++·人工智能·llm·agent·tools
_wxd6661 小时前
C++STL map与set
数据结构·c++