深入解析Redis核心数据结构:从String到Zset,解锁高性能排行榜实现
在当今数据驱动的时代,高效的数据存储与访问是应用性能的基石。Redis,作为一款广受欢迎的内存数据结构存储,以其丰富的数据类型和卓越的性能,成为缓存、会话存储乃至实时排行榜等场景的首选。与传统的MySQL或PostgreSQL等关系型数据库不同,Redis提供了更贴近业务逻辑的原子数据结构。本文将带你深入探索Redis从基础到进阶的核心数据结构,并重点剖析如何使用Zset(有序集合)构建一个高性能的实时排行榜系统。
Redis核心数据结构全景图
Redis的成功,很大程度上归功于其精心设计的核心数据结构。它们不仅仅是简单的键值对,而是针对不同场景优化的、功能丰富的原子类型。理解这些结构是进行有效数据库优化的第一步。
Redis最经典的五大基础类型包括:String(字符串)、Hash(哈希)、List(列表)、Set(集合)和Zset(有序集合)。每种类型都对应着不同的底层实现和适用场景。

为了更直观地对比它们的特性与典型应用,我们可以参考下表:
| 类型 | 结构存储的值 | 结构的读写功能 |
| String | 字符串、整数、浮点型 | 对一整个字符串或部分字符串读取,浮点型可以自增或自减 |
| Hash | 散列键值对 | 添加、获取、删除单个元素 |
| List | 一个链表,链表的节点包含字符串 | 可以对链表两侧进行push和pop操作,根据值查找或删除元素,还可以读取多个元素 |
| Set | 无序存储字符串的集合 | 可以做集合的交、并、差集;获取、添加、删除元素 |
| Zset | 有序存储字符串的集合 | 有序集合,可以根据元素的不同分数排序,可以添加、获取、删除元素,可以根据分值范围来获得元素 |
随着版本的迭代,Redis的功能边界不断扩展,引入了如GEO(地理位置)、HyperLogLog(基数统计)、Stream(流)和BitMap(位图)等高级数据类型。这些类型进一步拓宽了Redis的应用场景:
GEO:存储地图信息的场景,比如滴滴叫车
HyperLoglog:海量数据统计,比如百万计网页UV统计
Stream:消息队列,对比List而言,可以自行生成全局唯一ID;消费可以以消费组形式出现
BitMap:二值状态统计,比如签到
技术对比思考:当你需要复杂的事务和关联查询时,MySQL或PostgreSQL是更好的选择;当你处理无模式的文档数据时,MongoDB可能更合适。而当你需要极致的读写速度、原子操作和丰富的数据结构来支撑缓存、计数器、排行榜、消息队列等场景时,Redis的优势就无可替代。选择合适的工具,是架构设计的关键。
[AFFILIATE_SLOT_1]Zset(有序集合):排行榜的引擎
在众多数据结构中,Zset(Sorted Set)因其独特的“分数-成员”映射和自动排序能力,成为实现排行榜、延迟队列、范围查询等功能的利器。它与普通Set的最大区别在于,每个成员(Member)都关联一个分数(Score),Redis正是依据这个分数对成员进行从小到大的有序排列。
熟练掌握Zset的命令是应用它的前提。其核心命令主要围绕分数和排名展开:
SortedSet的常见命令有:
ZADD key score member:添加一个或多个元素到sorted set ,如果已经存在则更新其score值
ZREM key member:删除sorted set中的一个指定元素
ZSCORE key member : 获取sorted set中的指定元素的score值
ZRANK key member:获取sorted set 中的指定元素的排名
ZCARD key:获取sorted set中的元素个数
ZCOUNT key min max:统计score值在给定范围内的所有元素的个数
ZINCRBY key increment member:让sorted set中的指定元素自增,步长为指定的increment值
ZRANGE key min max:按照score排序后,获取指定排名范围内的元素
ZRANGEBYSCORE key min max:按照score排序后,获取指定score范围内的元素
ZDIFF.ZINTER.ZUNION:求差集.交集.并集
注意:所有的排名默认都是升序,如果要降序则在命令的Z后面添加REV即可,例如:
升序获取sorted set 中的指定元素的排名:ZRANK key member
降序获取sorted set 中的指定元素的排名:ZREVRANK key memeber
此处例子来自小林Coding
让我们通过一个具体的“博客文章点赞排行榜”案例,来实战演练Zset的应用。假设作者“小林”发布了五篇文章,初始点赞数如下:
# arcticle:1 文章获得了200个赞
> ZADD user:xiaolin:ranking 200 arcticle:1
(integer) 1
# arcticle:2 文章获得了40个赞
> ZADD user:xiaolin:ranking 40 arcticle:2
(integer) 1
# arcticle:3 文章获得了100个赞
> ZADD user:xiaolin:ranking 100 arcticle:3
(integer) 1
# arcticle:4 文章获得了50个赞
> ZADD user:xiaolin:ranking 50 arcticle:4
(integer) 1
# arcticle:5 文章获得了150个赞
> ZADD user:xiaolin:ranking 150 arcticle:5
(integer) 1
实战:构建文章点赞排行榜
1. 点赞加一操作
当用户为`article:4`点赞时,我们使用`ZINCRBY`命令原子性地增加其分数。这避免了在应用层先读后写的并发问题,是Redis原子操作带来的巨大优势。
> ZINCRBY user:xiaolin:ranking 1 arcticle:4
"51"
2. 查询单篇文章赞数
要查看某篇文章的具体点赞数,即其分数,使用`ZSCORE`命令。
> ZSCORE user:xiaolin:ranking arcticle:4
"50"
3. 获取Top-N排行榜
这是排行榜的核心功能。使用`ZREVRANGE`命令(按分数降序)可以轻松获取点赞最多的前3篇文章。
# WITHSCORES 表示把 score 也显示出来
> ZREVRANGE user:xiaolin:ranking 0 2 WITHSCORES
1) "arcticle:1"
2) "200"
3) "arcticle:5"
4) "150"
5) "arcticle:3"
6) "100"
4. 按分数区间查询
有时我们需要查看位于某个点赞区间的文章,例如100赞到200赞之间的,`ZRANGEBYSCORE`命令完美胜任。
> ZRANGEBYSCORE user:xiaolin:ranking 100 200 WITHSCORES
1) "arcticle:3"
2) "100"
3) "arcticle:5"
4) "150"
5) "arcticle:1"
6) "200"
⚠️ 性能提示:上述所有操作的时间复杂度基本都是O(log N),即使在数据量很大时也能保持高性能,这是Zset底层数据结构带来的红利。
深入原理:Zset与Set的差异及底层实现
理解Zset与Set的根本区别,能帮助我们在设计时做出正确选择。
- Zset (有序集合):元素唯一,且每个元素关联一个分数(Score)。集合根据分数自动排序,支持按分数范围、排名进行高效检索。它是为需要排序的场景而生的。
添加元素(带分数):(向 zset1 中添加元素,分数分别为 10、20、15)
按排名范围获取元素(从小到大):(返回所有元素及分数,结果按分数排序:"a" 10, "c" 15, "b" 20)
按分数范围获取元素:(返回分数 12-25 之间的元素:"c", "b")
增减元素分数:(将 "a" 的分数 +5,变为 15)
获取元素排名:(返回 "b" 的排名,从 0 开始,此处返回 2)
- Set (集合):元素唯一,但内部无序。它擅长进行集合运算,如求交集(SINTER)、并集(SUNION)、差集(SDIFF),常用于存储用户标签、共同好友等。
添加元素:(向 set1 中添加 3 个元素,重复添加会自动去重)
查看所有元素:(返回结果无序,如 ["b", "a", "c"])
判断元素是否存在:(存在返回 1,否则 0)
集合运算:(求两个集合的交集)、(求并集)
简而言之,Zset在Set的基础上,通过引入“分数”维度,解锁了一系列基于排序和范围查询的强大操作。
那么,Zset是如何实现这种高效排序和查询的呢?这要归功于其精巧的底层实现。在Redis早期版本中,Zset在元素较少时使用压缩列表(Ziplist)以节省内存,元素较多时则切换为跳跃表(Skip List)以保证操作效率。
若有序集合元素个数小于128并且每个元素的大小不超过64字节,则使用压缩列表
不满足以上条件则使用跳表
跳跃表是一种通过多级索引来加速查找的有序数据结构,它使得Zset的插入、删除、按分数查找等操作都能在近似O(log N)的时间内完成,同时实现比平衡树更简单的逻辑。
[AFFILIATE_SLOT_2]总结与最佳实践
Redis通过提供String、Hash、List、Set、Zset等核心数据结构,为开发者解决了各类常见的数据建模难题。其中,Zset凭借其有序性和高效的分数操作,成为实现实时排行榜、优先级队列等功能的“瑞士军刀”。
在实际应用中,请牢记:1) 根据数据特性和访问模式选择最合适的数据类型,是数据库优化的核心;2) 充分利用Redis命令的原子性,避免复杂的应用层事务;3) 了解底层实现(如Zset的跳表),有助于预判性能和在极端场景下的调优。无论是作为MySQL的缓存加速,还是作为独立的数据存储,深入理解Redis数据结构都将使你构建的系统更加健壮和高效。
ZADD zset1 10 "a" 20 "b" 15 "c"ZRANGE zset1 0 -1 WITHSCORESZRANGEBYSCORE zset1 12 25ZINCRBY zset1 5 "a"ZRANK zset1 "b"SADD set1 "a" "b" "c"SMEMBERS set1SISMEMBER set1 "a"SINTER set1 set2SUNION set1 set2
浙公网安备 33010602011771号