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