Redis有序集合ZSet #
一、有序集合概述 #
1.1 什么是有序集合 #
Redis有序集合是带分数的字符串集合,按分数排序:
- 有序:按分数从小到大排序
- 唯一:成员唯一,但分数可以相同
- 高效:O(log N)时间复杂度的插入、删除、查找
text
有序集合结构:
┌─────────────────────────────────────────────┐
│ Sorted Set(跳表实现) │
│ │
│ 成员 分数 │
│ ┌─────────────────────────────────────┐ │
│ │ "user1" → 100 │ │
│ │ "user2" → 200 │ │
│ │ "user3" → 300 │ │
│ │ "user4" → 400 │ │
│ │ "user5" → 500 │ │
│ └─────────────────────────────────────┘ │
│ │
│ 按分数排序,支持范围查询 │
└─────────────────────────────────────────────┘
最大成员数:2^32 - 1(约40亿)
1.2 有序集合编码 #
text
Redis有序集合的编码方式:
1. listpack(压缩列表)
┌─────────────────────────────────────────────┐
│ 元素数量少时使用 │
│ 条件:元素数<128,单元素<64字节 │
│ 内存连续,节省空间 │
└─────────────────────────────────────────────┘
2. skiplist + hashtable(跳表 + 哈希表)
┌─────────────────────────────────────────────┐
│ 元素数量多时使用 │
│ 跳表:支持范围查询,O(log N) │
│ 哈希表:支持O(1)查找成员分数 │
└─────────────────────────────────────────────┘
跳表结构:
┌─────────────────────────────────────────────┐
│ Level 3: Head ─────────────────→ Tail │
│ Level 2: Head ──────→ Node ────→ Tail │
│ Level 1: Head → Node → Node → Node → Tail│
│ Level 0: Head → Node → Node → Node → Tail│
└─────────────────────────────────────────────┘
二、基本操作 #
2.1 添加成员 #
bash
# ZADD: 添加成员
ZADD leaderboard 100 user1
# (integer) 1 新增成员数
# 添加多个成员
ZADD leaderboard 200 user2 300 user3 400 user4
# (integer) 3
# 更新已有成员的分数
ZADD leaderboard 150 user1
# (integer) 0 更新不计数
# ZADD选项(Redis 3.0.2+)
# XX: 只更新已存在的成员
ZADD leaderboard XX 500 user5
# (integer) 0 user5不存在,不添加
# NX: 只添加新成员
ZADD leaderboard NX 500 user1
# (integer) 0 user1已存在,不更新
# CH: 返回变化的成员数(包括更新)
ZADD leaderboard CH 160 user1
# (integer) 1
# INCR: 增加分数(相当于ZINCRBY)
ZADD leaderboard INCR 10 user1
# "170"
2.2 获取成员信息 #
bash
# ZSCORE: 获取成员分数
ZSCORE leaderboard user1
# "170"
ZSCORE leaderboard notexist
# (nil)
# ZRANK: 获取成员排名(从0开始,升序)
ZRANK leaderboard user1
# (integer) 0 第1名
# ZREVRANK: 获取成员排名(降序)
ZREVRANK leaderboard user1
# (integer) 3 倒数第4名
# ZCARD: 获取成员数量
ZCARD leaderboard
# (integer) 4
2.3 获取范围成员 #
bash
# 准备数据
ZADD leaderboard 100 user1 200 user2 300 user3 400 user4 500 user5
# ZRANGE: 获取指定排名范围的成员(升序)
ZRANGE leaderboard 0 2
# 1) "user1"
# 2) "user2"
# 3) "user3"
# 获取所有成员
ZRANGE leaderboard 0 -1
# 1) "user1"
# 2) "user2"
# 3) "user3"
# 4) "user4"
# 5) "user5"
# 带分数
ZRANGE leaderboard 0 2 WITHSCORES
# 1) "user1"
# 2) "100"
# 3) "user2"
# 4) "200"
# 5) "user3"
# 6) "300"
# ZREVRANGE: 获取指定排名范围的成员(降序)
ZREVRANGE leaderboard 0 2
# 1) "user5"
# 2) "user4"
# 3) "user3"
# ZREVRANGE WITHSCORES
ZREVRANGE leaderboard 0 2 WITHSCORES
# 1) "user5"
# 2) "500"
# 3) "user4"
# 4) "400"
# 5) "user3"
# 6) "300"
# Redis 6.2+ 新语法
ZRANGE leaderboard 0 -1 REV
ZRANGE leaderboard 0 -1 BYSCORE
ZRANGE leaderboard 0 -1 BYLEX
2.4 获取分数范围成员 #
bash
# ZRANGEBYSCORE: 获取指定分数范围的成员
ZRANGEBYSCORE leaderboard 200 400
# 1) "user2"
# 2) "user3"
# 3) "user4"
# 带分数
ZRANGEBYSCORE leaderboard 200 400 WITHSCORES
# 1) "user2"
# 2) "200"
# 3) "user3"
# 4) "300"
# 5) "user4"
# 6) "400"
# 开区间
ZRANGEBYSCORE leaderboard (200 (400
# 1) "user3"
# 无限范围
ZRANGEBYSCORE leaderboard -inf +inf
# 返回所有成员
ZRANGEBYSCORE leaderboard 300 +inf
# 1) "user3"
# 2) "user4"
# 3) "user5"
# 带LIMIT
ZRANGEBYSCORE leaderboard 0 500 LIMIT 0 2
# 1) "user1"
# 2) "user2"
# ZREVRANGEBYSCORE: 分数范围(降序)
ZREVRANGEBYSCORE leaderboard 400 200
# 1) "user4"
# 2) "user3"
# 3) "user2"
# ZCOUNT: 统计分数范围内的成员数
ZCOUNT leaderboard 200 400
# (integer) 3
2.5 删除成员 #
bash
# ZREM: 删除成员
ZREM leaderboard user1
# (integer) 1
ZREM leaderboard notexist
# (integer) 0
# 删除多个成员
ZREM leaderboard user2 user3
# (integer) 2
# ZREMRANGEBYRANK: 按排名范围删除
ZADD myset 1 a 2 b 3 c 4 d 5 e
ZREMRANGEBYRANK myset 0 1
# (integer) 2 删除排名0和1的成员
# ZREMRANGEBYSCORE: 按分数范围删除
ZADD myset 1 a 2 b 3 c 4 d 5 e
ZREMRANGEBYSCORE myset -inf (3
# (integer) 2 删除分数小于3的成员
# ZREMRANGEBYLEX: 按字典范围删除
ZREMRANGEBYLEX myset [a (c
# 删除字典范围[a, c)的成员
三、分数操作 #
3.1 增减分数 #
bash
# ZINCRBY: 增加分数
ZADD leaderboard 100 user1
ZINCRBY leaderboard 50 user1
# "150"
# 减少分数
ZINCRBY leaderboard -30 user1
# "120"
# 对不存在的成员操作
ZINCRBY leaderboard 100 newuser
# "100" 自动创建
3.2 分数统计 #
bash
# ZSCORE: 获取分数
ZSCORE leaderboard user1
# "120"
# ZCOUNT: 统计分数范围内的成员数
ZCOUNT leaderboard 100 200
# (integer) 3
四、字典序操作 #
4.1 字典序范围 #
bash
# 准备数据(分数相同)
ZADD myset 0 a 0 b 0 c 0 d 0 e 0 f
# ZRANGEBYLEX: 按字典序获取
ZRANGEBYLEX myset - +
# 1) "a"
# 2) "b"
# 3) "c"
# 4) "d"
# 5) "e"
# 6) "f"
# 指定范围
ZRANGEBYLEX myset [a [d
# 1) "a"
# 2) "b"
# 3) "c"
# 4) "d"
# 开区间
ZRANGEBYLEX myset (a (d
# 1) "b"
# 2) "c"
# ZREVRANGEBYLEX: 字典序(降序)
ZREVRANGEBYLEX myset + -
# 1) "f"
# 2) "e"
# 3) "d"
# 4) "c"
# 5) "b"
# 6) "a"
# ZLEXCOUNT: 统计字典序范围内的成员数
ZLEXCOUNT myset [a [d
# (integer) 4
五、集合运算 #
5.1 并集 #
bash
# 准备数据
ZADD zset1 1 a 2 b 3 c
ZADD zset2 2 b 3 c 4 d
# ZUNIONSTORE: 计算并集并存储
ZUNIONSTORE result 2 zset1 zset2
# (integer) 4
ZRANGE result 0 -1 WITHSCORES
# 1) "a"
# 2) "1"
# 3) "d"
# 4) "4"
# 5) "b"
# 6) "4" # 2+2=4
# 7) "c"
# 8) "6" # 3+3=6
# 指定权重
ZUNIONSTORE result 2 zset1 zset2 WEIGHTS 2 3
# zset1分数乘2,zset2分数乘3
# 指定聚合方式
ZUNIONSTORE result 2 zset1 zset2 AGGREGATE MIN
# 取最小分数
ZUNIONSTORE result 2 zset1 zset2 AGGREGATE MAX
# 取最大分数
ZUNIONSTORE result 2 zset1 zset2 AGGREGATE SUM
# 求和(默认)
5.2 交集 #
bash
# ZINTERSTORE: 计算交集并存储
ZINTERSTORE result 2 zset1 zset2
# (integer) 2
ZRANGE result 0 -1 WITHSCORES
# 1) "b"
# 2) "4" # 2+2=4
# 3) "c"
# 4) "6" # 3+3=6
# 指定权重和聚合方式
ZINTERSTORE result 2 zset1 zset2 WEIGHTS 1 2 AGGREGATE MAX
5.3 差集(Redis 6.2+) #
bash
# ZDIFFSTORE: 计算差集并存储
ZDIFFSTORE result 2 zset1 zset2
# zset1 - zset2
六、应用场景 #
6.1 排行榜 #
bash
# 游戏排行榜
ZADD game:leaderboard 1000 player:1
ZADD game:leaderboard 1500 player:2
ZADD game:leaderboard 2000 player:3
# 更新分数
ZINCRBY game:leaderboard 500 player:1
# 获取前10名
ZREVRANGE game:leaderboard 0 9 WITHSCORES
# 获取玩家排名
ZREVRANK game:leaderboard player:1
# (integer) 1 第2名
# 获取玩家分数
ZSCORE game:leaderboard player:1
# "1500"
# 获取玩家周围的排名
# 获取玩家排名
rank=$(ZRANK game:leaderboard player:1)
# 获取前后各2名
ZRANGE game:leaderboard $((rank-2)) $((rank+2)) WITHSCORES
6.2 延时队列 #
bash
# 添加延时任务(分数为执行时间戳)
ZADD delay:queue 1700001000 task:1
ZADD delay:queue 1700002000 task:2
ZADD delay:queue 1700003000 task:3
# 获取到期任务
current_time=$(date +%s)
ZRANGEBYSCORE delay:queue -inf $current_time
# 处理并删除
ZREMRANGEBYSCORE delay:queue -inf $current_time
6.3 滑动窗口限流 #
bash
# 记录请求时间戳
user_id=1001
current_time=$(date +%s%3N) # 毫秒时间戳
window=60000 # 60秒窗口
limit=100 # 限制100次
# 添加请求记录
ZADD rate:$user_id $current_time $current_time:random
# 删除窗口外的记录
ZREMRANGEBYSCORE rate:$user_id 0 $((current_time - window))
# 统计窗口内请求数
count=$(ZCARD rate:$user_id)
if [ $count -gt $limit ]; then
echo "限流"
else
echo "允许"
fi
6.4 标签权重 #
bash
# 文章标签权重
ZADD article:1001:tags 10 redis
ZADD article:1001:tags 8 database
ZADD article:1001:tags 5 nosql
# 获取标签(按权重排序)
ZREVRANGE article:1001:tags 0 -1 WITHSCORES
# 1) "redis"
# 2) "10"
# 3) "database"
# 4) "8"
# 5) "nosql"
# 6) "5"
6.5 实时热点 #
bash
# 热点文章
ZINCRBY hot:articles 1 article:1001
ZINCRBY hot:articles 1 article:1002
# 获取热点文章
ZREVRANGE hot:articles 0 9 WITHSCORES
# 定期清理
ZREMRANGEBYRANK hot:articles 0 -101 # 只保留前100
七、性能优化 #
7.1 避免大有序集合 #
bash
# 不推荐:超大有序集合
ZADD huge:zset 1 item1 ... 100000 item100000
# 推荐:定期清理
ZREMRANGEBYRANK myzset 0 -1001 # 只保留前1000
7.2 使用合适的数据结构 #
bash
# 只需要排序:使用ZSet
# 需要去重+排序:使用ZSet
# 只需要去重:使用Set
7.3 批量操作 #
bash
# 不推荐:多次单操作
ZADD myzset 1 a
ZADD myzset 2 b
ZADD myzset 3 c
# 推荐:批量操作
ZADD myzset 1 a 2 b 3 c
八、总结 #
有序集合操作命令:
| 命令 | 说明 |
|---|---|
| ZADD | 添加成员 |
| ZREM | 删除成员 |
| ZSCORE | 获取分数 |
| ZRANK | 获取排名(升序) |
| ZREVRANK | 获取排名(降序) |
| ZCARD | 获取成员数 |
| ZINCRBY | 增加分数 |
范围查询命令:
| 命令 | 说明 |
|---|---|
| ZRANGE | 按排名范围(升序) |
| ZREVRANGE | 按排名范围(降序) |
| ZRANGEBYSCORE | 按分数范围 |
| ZREVRANGEBYSCORE | 按分数范围(降序) |
| ZRANGEBYLEX | 按字典序 |
| ZCOUNT | 统计分数范围 |
集合运算命令:
| 命令 | 说明 |
|---|---|
| ZUNIONSTORE | 并集 |
| ZINTERSTORE | 交集 |
应用场景:
| 场景 | 实现方式 |
|---|---|
| 排行榜 | ZADD + ZREVRANGE |
| 延时队列 | ZADD + ZRANGEBYSCORE |
| 限流 | ZADD + ZREMRANGEBYSCORE |
| 热点统计 | ZINCRBY + ZREVRANGE |
下一步,让我们学习Redis的高级特性!
最后更新:2026-03-27