[TOC] #### 1. 前言 --- 做排行榜,你第一反应可能是:查出来排个序不就行了 ? MySQL 的 ORDER BY 确实能排,但每次查询都要排序,数据量大了很慢 ZSet 不一样,数据放进去就自动排好了,取出来直接用 #### 2. ZSet 结构 --- 语法格式: ```plaintext ZADD key score member [score member ...] ``` 每个元素带一个分数(score),Redis 自动按分数排序 ```bash zadd rank 100 zhangsan 90 lisi 95 wangwu ``` 从高到低(从低到高用 `zrange`): ```plaintext 127.0.0.1:6379> zrevrange rank 0 -1 withscores 1) "zhangsan" 2) "100" 3) "wangwu" 4) "95" 5) "lisi" 6) "90" ``` #### 3. 常用操作 --- 加分: ```bash zincrby rank 10 lisi # lisi 从90变成100 ``` 查排名: ```bash zrevrank rank zhangsan # 返回0,就是第一名 ``` 删除: ```bash zrem rank lisi ``` 按分数范围查: ```bash zrangebyscore rank 80 100 withscores # 80到100分的所有人 ``` #### 4. 排行榜实战 --- 文章热度排行,每次点赞 +1: ```bash zincrby hot:2026:05 1 article:1001 ``` 查本月 Top 10: ```bash zrevrange hot:2026:05 0 9 withscores ``` 查某篇文章排名: ```bash zrevrank hot:2026:05 article:1001 ``` 排行榜从零到上线,就这几行命令 其它场景 + 延时队列:score 存执行时间戳,zrangebyscore 取到期任务。后续详细讲。 + 滑动窗口限流:score 存请求时间戳,zcount 统计窗口内请求数。后续详细讲。 底层编码 + 元素少且小 → ziplist,紧凑省内存 + 元素多了 → skiplist(跳表)+ hashtable 跳表负责排序和范围查询,哈希表负责按 member 查分数 O(1) 为什么不用红黑树 ? 跳表实现更简单,范围查询更方便(zrange 直接取一段),而且性能差距不大 #### 5. 本文小结 --- 小总结: + ZSet 每个元素带分数,自动排序 + 排行榜首选:zadd 加分,zrevrange 查 Top N,zrevrank 查排名 + 底层 skiplist + hashtable + 排行榜、延时队列、限流都能做