【Redis】5种基础数据结构介绍及应用
考察频率 | 难度 |
60% | ⭐⭐ |
这个方向的问题也是非常基础的,所以一般不会直接被当做一个单独的问题。常见的形式是结合你简历上的项目或者场景题来提问,即实际应用场景、是否可以优化、如何选择等。
由于场景题和实际项目差异较大,所以本文就只做基础的讲解,如果笔者后面想到遇见过的场景题,也会做博客来讲解。
由于 Redis 数据结构的特殊性,每一个基础的数据结构其实都对应一个或者多个更底层是数据结构。由于对应关系比较复杂,出于篇幅以及复杂度考虑,更底层的讲解,笔者想要单独来写。
基础数据结构简介
在数据的江湖里,Redis无疑是那位神秘莫测、武功高强的武林盟主。今天,我们要介绍的就是Redis的五个顶级弟子(Redis 到现在已经有 9 种数据结构了),他们各怀绝技,行走江湖无往不利。话不多说,让我们一睹这五兄弟的风采!
Redis 有五种基本数据类型,这五种数据类型分别是:string(字符串)、hash(哈希)、list(列表)、set(集合)、sorted set(有序集合,也叫 zset)。
结构类型 | 结构存储的值 | 结构的读写能力 |
String | 可以是字符串、整数或浮点数 | 对整个字符串或者字符串的一部分进行操作;对整数或浮点数进行自增或自减操作; |
List | 一个链表,链表上的每个节点都包含一个字符串 | 对链表的两端进行 push 和 pop 操作,读取单个或多个元素;根据值查找或删除元素; |
Set | 包含字符串的无序集合 | 字符串的集合,包含基础的方法有看是否存在添加、获取、删除;还包含计算交集、并集、差集等; |
Hash | 包含键值对的无序散列表 | 包含方法有添加、获取、删除单个元素; |
Zset | 和散列一样,用于存储键值对 | 字符串成员与浮点数分数之间的有序映射;元素的排列顺序由分数的大小决定;包含方法有添加、获取、删除单个元素以及根据分值范围或成员来获取元素; |
基础数据结构详解
内容其实比较简单,我觉得理解的重点在于这个结构怎么用,能够用来做什么?所以我在梳理时,围绕图例,命令,执行和场景来阐述。
String字符串
String 是 Redis 中最基本的数据类型,一个 key 对应一个 value。
String 是最基本的 key-value 结构,key 是唯一标识,value 是具体的值,value其实不仅是字符串,也可以是数字(整数或浮点数),value 最多可以容纳的数据长度是 512M
。
常见指令
命令 | 介绍 |
---|---|
SET key value | 设置指定 key 的值 |
SETNX key value | 只有在 key 不存在时设置 key 的值 |
GET key | 获取指定 key 的值 |
MSET key1 value1 key2 value2 ... | 设置一个或多个指定 key 的值 |
MGET key1 key2 ... | 获取一个或多个指定 key 的值 |
STRLEN key | 返回 key 所储存的字符串值的长度 |
INCR key | 将 key 中储存的数字值(整型和浮点型)增一 |
DECR key | 将 key 中储存的数字值(整型和浮点型)减一 |
INCRBY/DECRBY key increment | 将 key 中储存的数字值(整型和浮点型)加/减 increment |
EXISTS key | 判断指定 key 是否存在 |
DEL key(通用) | 删除指定的 key |
EXPIRE key seconds(通用) | 给指定 key 设置过期时间 |
APPEND key value | 给指定 key 后面追加值 value |
应用场景
-
缓存数据:字符串是缓存数据的最常用数据结构,比如缓存用户信息、产品详情等。
-
计数器:使用INCR、DECR等命令,可以轻松实现各种计数功能,如网站访问量、点赞数等。
-
会话存储:将用户会话信息存储在字符串中,便于快速读取和更新。
- 共享 Session 信息
List列表
Redis中的List其实就是链表(Redis用双端链表实现List)。
List 列表是简单的字符串列表,按照插入顺序排序,可以从头部或尾部向 List 列表添加元素。
使用List结构,我们可以轻松地实现最新消息排队功能(比如新浪微博的TimeLine)。List的另一个应用就是消息队列,可以利用List的 PUSH 操作,将任务存放在List中,然后工作线程再用 POP 操作将任务取出进行执行。
常见指令
命令 | 介绍 |
---|---|
LPUSH key value1 value2 .. | 在指定列表的头部(左边)添加一个或多个元素 |
RPUSH key value1 value2 ... | 在指定列表的尾部(右边)添加一个或多个元素 |
LSET key index value | 将指定列表索引 index 位置的值设置为 value |
LPOP key | 移除并获取指定列表的第一个元素(最左边) |
RPOP key | 移除并获取指定列表的最后一个元素(最右边) |
LLEN key | 获取列表元素数量 |
LRANGE key start end | 获取列表 start 和 end 之间 的元素 |
应用场景
-
任务队列:列表可以用作任务队列,支持从两端插入和删除元素。
-
消息队列:可以用列表来实现简单的消息队列,保证消息的顺序性。
-
日志存储:将日志条目存储在列表中,便于按时间顺序追加和读取。
Set集合
Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
Set 类型时无需且唯一的键值组合,它的存储顺序不会按照插入的先后顺序进行存储,即是无序的。
一个集合最多可以存储 2^31 - 1 个元素。概念和数学中的集合基本类似,可以交集、并集、差集等等,所以 Set 类型除了支持集合内的增删改查,同时还支持多个集合取交集、并集、差集。
Set 类型和 List 类型的区别如下:
- List 可以存储重复元素,Set 只能存储非重复元素。
- List 是按照元素的先后顺序存储元素的,而 Set 则是无序方式存储元素的。
常见指令
命令 | 介绍 |
---|---|
SADD key member1 member2 ... | 向指定集合添加一个或多个元素 |
SMEMBERS key | 获取指定集合中的所有元素 |
SCARD key | 获取指定集合的元素数量 |
SISMEMBER key member | 判断指定元素是否在指定集合中 |
SINTER key1 key2 ... | 获取给定所有集合的交集 |
SINTERSTORE destination key1 key2 ... | 将给定所有集合的交集存储在 destination 中 |
SUNION key1 key2 ... | 获取给定所有集合的并集 |
SUNIONSTORE destination key1 key2 ... | 将给定所有集合的并集存储在 destination 中 |
SDIFF key1 key2 ... | 获取给定所有集合的差集 |
SDIFFSTORE destination key1 key2 ... | 将给定所有集合的差集存储在 destination 中 |
SPOP key count | 随机移除并获取指定集合中一个或多个元素 |
SRANDMEMBER key count | 随机获取指定集合中指定数量的元素 |
应用场景
集合的主要几个特性:无序、不可重复、支持并交差集等。借助这几个特性,常见的使用场景为:
-
去重:集合天生不允许重复元素,非常适合用于去重场景。
-
标签管理:适合存储用户标签、商品标签等。
-
社交网络:可以用来管理好友关系、共同兴趣等。
但是这里有一个需要注意的风险。Set 的并集、交集和差集的计算复杂度较高,在数据量较大的情况下,如果直接执行这些计算,会导致 Redis 示例阻塞。
在主从集群中,为了避免主库因为 Set 做聚合计算(并集、交集和差集)时导致主库被阻止,我们可以选择一个从库完成聚合统计,或者把数据返回给客户端,由客户端来完成聚合统计。
Hash散列
Hash 是一个 String 类型的 field(字段) 和 value(值) 的映射表,Hash 特别适合用于存储对象。
Hash 与 String 对象的区别如下图所示:
常见指令
命令 | 介绍 |
---|---|
HSET key field value | 设置指定哈希表中指定字段的值 |
HSETNX key field value | 只有指定字段不存在时设置指定字段的值 |
HMSET key field1 value1 field2 value2 ... | 同时将一个或多个 field-value (域-值)对设置到指定哈希表中 |
HGET key field | 获取指定哈希表中指定字段的值 |
HMGET key field1 field2 ... | 获取指定哈希表中一个或者多个指定字段的值 |
HGETALL key | 获取指定哈希表中所有的键值对 |
HEXISTS key field | 查看指定哈希表中指定的字段是否存在 |
HDEL key field1 field2 ... | 删除一个或多个哈希表字段 |
HLEN key | 获取指定哈希表中字段的数量 |
应用场景
-
存储对象:哈希非常适合存储具有多个字段的对象,如用户信息、商品信息等。
-
轻量级的数据存储:可以存储相对较小的数据,如配置项、状态信息等。
-
减少内存消耗:对于多字段数据,使用哈希可以比字符串节省内存。
Zset有序集合
Zset 和 Set 一样也是 String 类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个 double 类型的分数。Redis 正是通过分数来为集合中的成员进行从小到大的排序。
有序集合的成员是唯一的, 但分数(score)却可以重复。有序集合是通过两种数据结构实现:
- 压缩列表(ziplist): ziplist是为了提高存储效率而设计的一种特殊编码的双向链表。它可以存储字符串或者整数,存储整数时是采用整数的二进制而不是字符串形式存储。它能在O(1)的时间复杂度下完成list两端的push和pop操作。但是因为每次操作都需要重新分配ziplist的内存,所以实际复杂度和ziplist的内存使用量相关
- 跳跃表(zSkiplist): 跳跃表的性能可以保证在查找,删除,添加等操作的时候在对数期望时间内完成,这个性能是可以和平衡树来相比较的,而且在实现方面比平衡树要优雅,这是采用跳跃表的主要原因。跳跃表的复杂度是O(log(n))。
常见指令
命令 | 介绍 |
---|---|
ZADD key score1 member1 score2 member2 ... | 向指定有序集合添加一个或多个元素 |
ZCARD KEY | 获取指定有序集合的元素数量 |
ZSCORE key member | 获取指定有序集合中指定元素的 score 值 |
ZINTERSTORE destination numkeys key1 key2 ... | 将给定所有有序集合的交集存储在 destination 中,对相同元素对应的 score 值进行 SUM 聚合操作,numkeys 为集合数量 |
ZUNIONSTORE destination numkeys key1 key2 ... | 求并集,其它和 ZINTERSTORE 类似 |
ZDIFF destination numkeys key1 key2 ... | 求差集,其它和 ZINTERSTORE 类似 |
ZRANGE key start end | 获取指定有序集合 start 和 end 之间的元素(score 从低到高) |
ZREVRANGE key start end | 获取指定有序集合 start 和 end 之间的元素(score 从高到底) |
ZREVRANK key member | 获取指定有序集合中指定元素的排名(score 从大到小排序) |
应用场景
Zset 类型(Sorted Set,有序集合) 可以根据元素的权重来排序,我们可以自己来决定每个元素的权重值。在面对需要展示最新列表、排行榜等场景时,如果数据更新频繁或者需要分页显示,可以优先考虑使用 Zset。
-
排行榜:有序集合非常适合实现排行榜,按分数排序展示前N名用户。
-
带权重的数据存储:适用于需要按权重排序的数据,如评分系统。
-
延迟队列:可以根据分数(时间戳)实现延迟任务队列。
总结
Redis数据结构五兄弟,各个身怀绝技,各有千秋。无论是字符串的快剑手、哈希的万花筒、列表的快马、集合的独行侠,还是有序集合的智者,他们都在数据江湖中扮演着不可或缺的角色。希望这篇介绍能让你对Redis的五种基础数据结构有一个更生动形象的了解。让我们一起在Redis的世界中,成为数据江湖的侠客吧!
哈哈,抽象的标题、抽象的开头和抽象的结尾,笔者实在是懒得去想这些东西要怎么写了,但是又想搞一篇有趣的文章,于是去请教了万能的人工智能 ChatGPt,还可以吧,描述很传神。
本文修改自笔者之前在自己博客网站上发布过的一篇博客,那篇博客是一个史诗长度的博客,详细介绍了每一种基础数据结构的基本介绍、常见命令、底层实现、应用场景以及示例,但是由于内容比较多,所以会显得很杂乱,这也是重新修改发布的原因。
本文主要讲解了基础数据结构的基础内容,并没有很深入,关于应用、实现部分的内容,我还是想要单独来写博客讲解,尽量讲清楚。所以,本文其实算是一篇基础的引子,后面要涉及到的其他博客会在后面加上链接。
🎉 如果这篇文章对你有帮助,点赞👍 收藏⭐ 关注✅ 哦,创作不易,感谢!😀