Redis 的各种数据类型是其强大功能的核心,每种类型都针对特定的应用场景进行了优化。理解这些场景是高效使用 Redis 的关键。
下面我将详细介绍 Redis 主要数据类型及其经典的实际应用场景。
1. String(字符串)
这是最基础的数据类型,可以存储字符串、整数或浮点数。一个 key 对应一个 value。
特点:二进制安全,意味着可以存储任何数据,比如图片或序列化后的对象。
应用场景:
- 缓存(Cache):这是最经典的场景。将数据库查询结果、复杂的计算结果的序列化字符串(如 JSON)存入 Redis,设置一个过期时间,后续请求直接读取,极大减轻后端数据库压力。
SET user:1001 '{"name": "Alice", "email": "alice@example.com"}' EX 3600
- 计数器(Counter):利用
INCR,DECR,INCRBY等命令实现原子性操作,无需担心多线程/多进程竞争问题。- 文章点赞数:
INCR article:1001:likes - 用户粉丝数:
INCR user:1001:followers - 网站访问量:
INCR site_total_visits
- 文章点赞数:
- 分布式锁(Distributed Lock):简单版本的分布式锁可以使用
SET key value NX EX seconds来实现。NX表示只有当 key 不存在时才设置,这可以保证只有一个客户端能设置成功,即获取到锁。 - Session 存储:在分布式或微服务架构中,将用户的登录会话信息(Session)集中存储在 Redis 中,所有服务节点都可以访问,实现状态分离。
2. Hash(哈希)
类似于 Java 中的 Map 或 Python 中的 dict,是一个键值对集合,非常适合存储对象。
特点:可以将多个 field-value 对存储在一个 key 下。
应用场景:
- 存储对象信息:这是 Hash 的最佳场景。存储一个对象的多个字段,比将整个对象序列化成 String 再存储更高效,且可以单独操作某个字段。
- 用户信息:
HMSET user:1001 name "Alice" age "30" email "alice@example.com" HGET user:1001 name # 只获取名字,无需读取整个对象 HINCRBY user:1001 age 1 # 只修改年龄字段 - 商品信息:
商品ID -> {name: "iPhone", price: 5999, stock: 100}
- 用户信息:
- 购物车(Shopping Cart):
- Key:
cart:user_id - Field:
product_id - Value:
商品数量 - 操作:
HSET cart:1001 123 2(用户1001的商品123数量为2),HINCRBY cart:1001 123 1(增加一件)。
- Key:
3. List(列表)
一个简单的字符串列表,按插入顺序排序,你可以在头部(left)或尾部(right)添加元素。
特点:双向链表实现,意味着头尾操作性能极高,但访问中间元素性能较差。
应用场景:
- 消息队列(Message Queue):使用
LPUSH+BRPOP组合可以实现一个简单的 FIFO(先进先出)队列。- 生产者:
LPUSH task_queue <task_data> - 消费者:
BRPOP task_queue 30(阻塞30秒获取任务)
- 生产者:
- 最新列表(Timeline):比如最新文章、最新新闻、最新评论等。
LPUSH latest_news <news_id>添加新新闻。LRANGE latest_news 0 9获取最新的10条新闻。因为LPUSH保证了新元素在列表头部,这个操作非常快。
- 记录操作日志:用户的操作记录可以
LPUSH到一个 list 中,方便后续审计或分析。
4. Set(集合)
Redis 的 Set 是 String 类型的无序集合,通过哈希表实现,元素不重复。
特点:提供高效的集合操作,如求交集、并集、差集。
应用场景:
- 标签(Tag):为用户、文章、商品等添加标签。
SADD article:1001:tags "tech" "redis" "database"(给文章1001打标签)SMEMBERS article:1001:tags(获取文章的所有标签)SINTER user:1001:followed_tags user:1002:followed_tags(找出两个用户共同关注的标签)
- 共同关注 / 好友推荐:
SADD user:1001:follows 1002 1003 1004(记录用户1001的关注列表)SADD user:1002:follows 1003 1005 1006(记录用户1002的关注列表)SINTER user:1001:follows user:1002:follows->{1003}(找出共同关注)
- 抽奖/秒杀:去重:确保一个用户只能参与一次秒杀或抽奖。
SADD lottery:20231001 user_id(用户参与活动,自动去重)SMEMBERS lottery:20231001(获取所有参与用户)SRANDMEMBER lottery:20231001 3(随机抽取3名中奖者)
5. Sorted Set(有序集合 / ZSet)
和 Set 一样也是 String 类型元素的集合,且不允许重复。但每个元素都会关联一个 score(分数),Redis 通过这个分数来为集合中的成员进行从小到大排序。
特点:元素唯一,但分数可以重复。性能非常高,基于**跳跃表(Skip List)**实现。
应用场景:
- 排行榜(Leaderboard):这是 ZSet 的杀手级应用。
- 游戏排行榜:
ZADD leaderboard 1000 "player1" 950 "player2"(更新玩家分数) ZREVRANGE leaderboard 0 9 WITHSCORES(获取前十名玩家及分数,降序排列)ZRANK leaderboard "player1"(获取某个玩家的排名)
- 游戏排行榜:
- 带权重的队列:将任务的执行时间作为
score,消费者使用ZRANGEBYSCORE命令来获取到期的任务进行处理。 - 实时排名系统:如新闻的热度排名,热度值(点赞、评论、浏览的综合计算)作为
score,新闻ID作为成员,可以非常高效地获取Top N的热点新闻。
6. HyperLogLog(基数统计)
用于做基数统计的算法。它的优点是,在输入元素的数量或者体积非常大时,计算基数所需的空间总是固定且很小的。
特点:只能统计一个集合的近似基数(去重后的元素个数),有小于 1% 的误差,无法获取元素本身。
应用场景:
- 大规模数据去重统计:统计网站的 UV(Unique Visitor,独立访客数)。
- 传统用 Set 会非常耗费内存,因为要存储所有用户ID。
- 使用 HyperLogLog:
PFADD uv:20231001 user_ip_1 user_ip_2 user_ip_3 ... - 查询当天的UV:
PFCOUNT uv:20231001,即使有上亿次访问,也只需要约 12KB 内存。
7. Bitmaps(位图)
通过特殊的命令,可以把 String 类型当作一个由二进制位组成的数组来处理。
特点:极其节省空间,适合存储大量的布尔值。
应用场景:
- 用户签到(Attendance):
SETBIT attendance:user:1001:202310 5 1(记录用户1001在2023年10月的第6天签到了,偏移量从0开始)。BITCOUNT attendance:user:1001:202310(统计该用户本月签到次数)
- 活跃用户统计:统计连续打卡的用户等。
- 布隆过滤器(Bloom Filter):可以利用 Bitmaps 实现,用于大规模数据判重,判断某个元素“一定不存在”或“可能存在”。
8. Geospatial(地理空间)
专门用于存储和操作地理位置信息的数据类型,底层基于 Sorted Set 实现。
特点:可以存储经纬度,并计算两点间的距离、查找指定范围内的地点等。
应用场景:
- 附近的人(People Nearby):
GEOADD locations 116.405285 39.904989 "user1"(添加用户坐标)GEORADIUS locations 116.405285 39.904989 10 km WITHDIST(查找附近10公里内的人,并返回距离)
- 摇一摇、附近的餐厅、共享单车等所有基于地理位置的服务(LBS)。
总结
| 数据类型 | 特性 | 典型应用场景 |
|---|---|---|
| String | 单键单值 | 缓存、计数器、分布式锁、Session |
| Hash | 键值对集合 | 存储对象、购物车 |
| List | 有序、可重复 | 消息队列、最新列表、日志 |
| Set | 无序、唯一、集合运算 | 标签、共同好友、抽奖去重 |
| Sorted Set | 有序、唯一、按分排序 | 排行榜、带权重任务队列 |
| HyperLogLog | 基数统计 | 大规模UV统计 |
| Bitmaps | 位操作 | 用户签到、布尔状态统计 |
| Geospatial | 地理位置 | 附近的人、地点搜索 |
选择合适的数据类型非常重要,它不仅能提升性能(更快的操作速度),还能节省大量的内存空间。在设计时,应优先考虑使用更符合场景的复合数据结构(如 Hash, Set, ZSet),而不是简单地将所有数据都序列化后存入 String。

1198

被折叠的 条评论
为什么被折叠?



