Redis常见数据类型及应用场景
好的,我们来详细讲解 Redis 的数据结构及其应用场景。Redis 的强大之处不仅仅在于它支持简单的键值对,更在于它提供了丰富的数据结构,每种结构都针对特定类型的应用场景进行了优化。
核心数据结构与应用场景
Redis 主要支持以下五种核心数据结构:String(字符串)、Hash(哈希)、List(列表)、Set(集合)、Sorted Set(有序集合)。此外,还有 Bitmaps、HyperLogLogs、Streams 等更高级的结构。
1. String(字符串)
这是最简单也是最基础的数据类型。一个 Key 对应一个 Value。Value 不仅是字符串,也可以是数字(整数或浮点数),并且可以对数字进行自增/自减操作。
- 内部实现:基于简单动态字符串(SDS)实现,可以修改的字符串,预分配内存,减少内存频繁分配。
- 常用命令:
SET
,GET
,MSET
,MGET
,INCR
,DECR
,INCRBY
应用场景:
- 缓存:最经典的场景。将数据库查询结果、热点数据、会话信息(Session)等序列化后存入 String,加快访问速度。
SET user:1001 "{name: 'Alice', email: 'alice@example.com'}"
- 计数器:利用
INCR
、DECR
命令实现点赞数、浏览数、库存计数等。这些操作是原子性的,非常适合高并发场景。INCR article:1001:views
- 分布式锁:利用
SET key value NX EX seconds
命令(当 key 不存在时设置,并设置过期时间)可以实现简单的分布式锁。 - Session 共享:在集群服务中,将用户的登录会话信息集中存储在 Redis 中,实现多台服务器共享 Session。
2. Hash(哈希)
类似于 Java 中的 Map<String, Object>
,是一组键值对的集合。非常适合存储对象。
- 内部实现:底层有两种编码方式:
ziplist
(压缩列表,在元素少、体积小时使用)和hashtable
(哈希表)。 - 常用命令:
HSET
,HGET
,HMSET
,HMGET
,HGETALL
,HINCRBY
应用场景:
- 存储对象:存储用户信息、商品信息等需要多个字段的对象。相比将整个对象序列化成 String,Hash 可以单独获取、修改某个字段,更节省网络带宽和存储空间。
HSET user:1001 name "Alice" age "30" email "alice@example.com"
HGET user:1001 name
-> “Alice”
- 购物车:以用户ID为 Key,商品ID为 Field,商品数量为 Value。
HSET cart:1001 product:5001 3
(用户1001的商品5001数量为3)HINCRBY cart:1001 product:5001 1
(增加1件)
3. List(列表)
一个简单的字符串列表,按插入顺序排序。你可以从列表的头部(左边)或尾部(右边)添加元素。
- 内部实现:底层是
quicklist
(快速列表),它是多个ziplist
通过双向指针组成的链表,兼顾了空间效率和插入性能。 - 常用命令:
LPUSH
,RPUSH
,LPOP
,RPOP
,LRANGE
,BLPOP
(阻塞操作)
应用场景:
- 消息队列:利用
LPUSH
+BRPOP
可以实现一个简单的 FIFO(先进先出)队列。生产者从左边推入消息,消费者用阻塞方式从右边取出消息。 - 最新列表:例如最新文章、最新评论、朋友圈时间线。利用
LPUSH
加入新元素,再用LRANGE 0 9
获取最新的10条。LPUSH news:latest "News ID 10086"
- 记录用户操作历史:例如用户的最近搜索、最近浏览记录。
4. Set(集合)
Redis 的 Set 是 String 类型的无序集合,集合内的元素是唯一的,不允许重复。
- 内部实现:底层是
intset
(整数集合,当元素都是整数且数量少时)或hashtable
(哈希表,value 为 null)。 - 常用命令:
SADD
,SMEMBERS
,SISMEMBER
,SINTER
(交集),SUNION
(并集),SDIFF
(差集)
应用场景:
- 标签(Tag):给用户、文章等对象打标签。可以很方便地求交集、并集等。
SADD article:1001:tags "tech" "redis" "database"
SADD user:1002:tags "tech" "python"
SINTER article:1001:tags user:1002:tags
-> 获取共同标签 “tech”
- 共同关注/好友:利用
SINTER
可以轻松求出两个用户的共同好友。 - 抽奖/秒杀:利用
SADD
将所有参与用户ID加入,可以自动保证唯一性,不会重复添加。SMEMBERS
可以列出所有参与者。 - 黑白名单:将需要过滤的 ID 放入 Set,用
SISMEMBER
快速判断某个 ID 是否在名单内。
5. Sorted Set(有序集合 / ZSet)
与 Set 类似,也是 String 类型元素的集合,且不允许重复。但每个元素都会关联一个 double
类型的分数(score)。Redis 正是通过分数来为集合中的成员进行从小到大的排序。成员是唯一的,但分数可以重复。
- 内部实现:底层是
ziplist
(压缩列表)或skiplist
(跳跃表) +dict
(字典)的组合,跳跃表保证范围查询的效率,字典保证按成员查询的效率。 - 常用命令:
ZADD
,ZRANGE
(按分数正序),ZREVRANGE
(按分数倒序),ZRANK
,ZREVRANK
,ZRANGEBYSCORE
应用场景:
- 排行榜:这是最经典的应用场景。将分数设为点击量、销量、热度等,自动进行排序。
ZADD leaderboard 100 "player1" 200 "player2"
ZREVRANGE leaderboard 0 9 WITHSCORES
-> 获取排行榜前十名
- 带权重的队列:分数可以作为优先级,实现优先级队列。
- 范围查找:例如处理成绩表,快速查找分数在 [90, 100] 之间的学生。
ZRANGEBYSCORE grades 90 100
- 延时任务:将任务的执行时间作为 score,用一个进程轮询获取到期的任务(
ZRANGEBYSCORE key 0 <当前时间戳>
)。
其他高级数据结构
- Bitmaps(位图): 本质上是 String,但可以对字符串的位进行操作。适用于大量布尔值的存储,如用户签到记录(每天1bit)、活跃用户统计,极其节省空间。
- HyperLogLogs: 用于做基数统计(估算一个集合中不重复元素的个数),标准误差仅0.81%。优点是非常节省空间。适用于统计网站的 UV(独立访客)、搜索关键词的不重复数量等,
PFADD
,PFCOUNT
,PFMERGE
。 - Geospatial(地理空间): 可以存储地理坐标,并计算距离、查找范围内成员等。适用于附近的人、地理位置推荐。
- Streams(流): Redis 5.0 引入,专门为消息队列设计,支持多消费者组、消息持久化、确认机制,功能比 List 更强大,是更专业的消息队列解决方案。
总结与选择建议
数据结构 | 特性 | 典型应用场景 |
---|---|---|
String | 简单键值,支持数字和位操作 | 缓存、计数器、分布式锁 |
Hash | 适合存储对象,可部分更新 | 用户信息、购物车、配置项 |
List | 有序、可重复,支持阻塞操作 | 消息队列、最新列表、历史记录 |
Set | 无序、唯一,支持集合运算 | 标签、共同好友、抽奖、黑白名单 |
Sorted Set | 唯一、有序(按分数排序) | 排行榜、优先级队列、范围查找 |
Bitmaps | 极省空间的布尔状态存储 | 用户签到、活跃用户统计 |
HyperLogLog | 极省空间的基数估算 | 网站UV统计 |
Streams | 持久化的消息流 | 复杂消息队列 |
选择时考虑以下几点:
- 数据形态:是需要一个对象(Hash)、一个列表(List)、一个不重复集合(Set)还是一个带排序的集合(ZSet)?
- 操作类型:是需要频繁读取部分字段(Hash),还是需要排序(ZSet),或是需要集合运算(Set)?
- 性能与效率:String 存储序列化对象虽然简单,但修改一个字段就需要整个读写,不如 Hash 高效。在元素较少时,Redis 会使用更紧凑的编码(如 ziplist)来节省内存。