Redis 大数据类型实战详解
Redis 不仅仅是一个简单的键值缓存系统,其强大的 丰富数据结构 是它在现代分布式系统中广泛应用的核心原因。相比传统键值存储仅支持字符串,Redis 提供了多种“大数据类型”(Data Structures),每种结构针对特定业务场景进行了高度优化。
本文将系统性地介绍 Redis 的 5 大核心数据类型 及其扩展类型,结合真实应用场景、命令用法和最佳实践,帮助你掌握 Redis 在实际项目中的高级用法。
一、Redis 支持的数据类型概览
🟠 String | 最基础类型,二进制安全 | 缓存、计数器、分布式锁 |
🟡 Hash | 键值对集合(field-value) | 存储对象(如用户信息) |
🔵 List | 有序、可重复的队列 | 消息队列、最新动态 |
🔴 Set | 无序、唯一元素集合 | 标签、好友关系、去重 |
🟣 Sorted Set(ZSet) | 有序集合(score 排序) | 排行榜、延迟队列 |
⚪ Bitmap | 位数组 | 用户签到、活跃统计 |
🌍 Geo | 地理位置坐标 | 附近的人、LBS 搜索 |
📊 HyperLogLog | 基数估算 | UV 统计 |
📥 Stream | 日志流结构 | 消息队列、事件流 |
✅ 所有类型都支持过期时间(TTL)、原子操作和高性能读写。
二、各大数据类型的实战详解
1. String:最基础但最常用
✅ 特性:
- 二进制安全,最大支持 512MB
- 支持自增/自减、追加、位操作等
🔧 常用命令:
SET user:1001 "{'name': 'Alice'}"
GET user:1001
INCR counter:page_view # 访问量+1
DECRBY stock 10 # 库存减10
APPEND log "new line\\n" # 追加内容
GETRANGE user:1001 0 9 # 截取字符串
💡 实战场景:
- 缓存热点数据(页面、用户信息)
- 计数器(PV、UV、库存)
- 分布式锁(SET key value NX EX 10)
- 共享 Session(SET session:abc "user_id=123")
⚠️ 注意事项:
- 避免存储过大的 String(>1MB),影响网络传输和阻塞主线程
- 大对象建议拆分或使用压缩
2. Hash:存储对象的理想选择
✅ 特性:
- 类似于 Map,适合存储对象的多个字段
- 节省内存(相比多个 String)
🔧 常用命令:
HSET user:1001 name Alice age 25 city Beijing
HGET user:1001 name
HMGET user:1001 name age city
HGETALL user:1001
HINCRBY user:1001 age 1
💡 实战场景:
- 用户资料、商品信息、配置项存储
- 替代多个 String 存储对象字段,减少 key 数量
✅ 优势:
- 可以单独更新某个 field,无需读取整个对象
- 内存效率高(尤其是小对象)
⚠️ 注意事项:
- 单个 Hash 中 field 数量不宜过多(建议 < 1万)
- 大 Hash 可能导致阻塞,可考虑分片(如 user:1001:profile:1, user:1001:profile:2)
3. List:双向链表,实现队列/栈
✅ 特性:
- 有序、可重复、支持从两端操作
- 底层为双向链表(Redis 7.0+ 优化为 Packed List)
🔧 常用命令:
LPUSH news:latest "article_1"
RPUSH news:latest "article_2"
LRANGE news:latest 0 9 # 获取前10条
LPOP queue # 消费任务
RPOP queue
BLPOP queue 5 # 阻塞式弹出(超时5秒)
💡 实战场景:
- 消息队列(生产者-消费者模型)
- 最新动态/朋友圈(LPUSH + LRANGE)
- 任务队列(异步处理)
- 栈结构(撤销操作)
⚠️ 注意事项:
- 不适合做“分页查询中间数据”,LRANGE 偏移量大时性能差
- 推荐使用 Stream 替代 List 做可靠消息队列(支持消费者组、持久化)
4. Set:无序唯一集合
✅ 特性:
- 元素唯一、无序、支持集合运算
- 底层为哈希表或整数集合(intset)
🔧 常用命令:
SADD tags:article:1 java redis spring
SISMEMBER tags:article:1 java # 判断是否包含
SMEMBERS tags:article:1 # 获取所有标签
SADD user:1001:friends 2001 2002 2003
SADD user:2001:friends 1001 3001
SINTER user:1001:friends user:2001:friends # 共同好友
SUNION tags:* # 所有标签并集
SDIFF set1 set2 # 差集
💡 实战场景:
- 标签系统(文章标签、用户兴趣)
- 好友关系、关注列表
- 去重(如已读文章 ID)
- 权限控制(用户角色集合)
✅ 优势:
- 支持交集、并集、差集等数学运算
- 查询元素是否存在为 O(1)
⚠️ 注意事项:
- 不保证顺序,如需排序使用 Sorted Set
- 大集合操作(如 SUNION)可能阻塞,建议在从节点执行或分批处理
5. Sorted Set(ZSet):带权重的有序集合
✅ 特性:
- 每个元素关联一个 score(浮点数),按 score 排序
- 支持范围查询、排名计算
🔧 常用命令:
ZADD leaderboard 1000 "Alice"
ZADD leaderboard 950 "Bob"
ZADD leaderboard 1050 "Charlie"
ZRANGE leaderboard 0 -1 WITHSCORES # 升序
ZREVRANGE leaderboard 0 -1 WITHSCORES # 降序
ZRANK leaderboard "Bob" # 排名(从0开始)
ZSCORE leaderboard "Alice" # 查看分数
ZREM leaderboard "Bob" # 删除成员
ZCOUNT leaderboard 900 1000 # 分数范围内的成员数
💡 实战场景:
- 排行榜(游戏积分、销售榜、热搜)
- 延迟队列(score = 执行时间戳)
- 滑动窗口限流(score 为时间,定期清理过期请求)
✅ 高级技巧:延迟队列实现
# 添加延迟任务(10分钟后执行)
ZADD delay_queue $(($(date +%s) + 600)) "task:email:123"
# 轮询执行到期任务
ZRANGEBYSCORE delay_queue 0 $(date +%s)
# 执行后删除 ZREM
⚠️ 注意事项:
- 插入/删除 O(log N),查询排名 O(log N)
- 大规模 ZSet 建议设置过期策略或分片
6. Bitmap:位图,极致节省空间
✅ 特性:
- 每个 bit 表示一个状态(0/1)
- 节省空间,适合大规模布尔统计
🔧 常用命令:
# 用户 1001 第 5 天签到
SETBIT user:1001:sign:2024-04 4 1
# 检查是否签到
GETBIT user:1001:sign:2024-04 4
# 统计本月签到天数
BITCOUNT user:1001:sign:2024-04
# 计算连续签到(需 Lua 脚本)
💡 实战场景:
- 用户签到系统
- 活跃用户统计(每日 UV 布尔标记)
- 用户行为标记(是否看过某内容)
✅ 优势:
- 1MB 可表示 800+ 万用户某天的状态
- 支持位运算(AND/OR/NOT)做群体分析
⚠️ 注意事项:
- 位索引从 0 开始,注意计算偏移
- 不适合频繁随机访问单个 bit(网络开销)
7. Geo:地理位置服务
✅ 特性:
- 基于 Sorted Set 实现,score 为 GeoHash 编码
- 支持距离计算、范围搜索
🔧 常用命令:
GEOADD cities 116.405285 39.904989 "Beijing"
GEOADD cities 121.473701 31.230416 "Shanghai"
# 计算距离
GEODIST cities "Beijing" "Shanghai" km
# 查找附近城市(500km 内)
GEORADIUS cities 116.405285 39.904989 500 km WITHDIST
# 查找附近的人
GEORADIUS users:locations 113.944 22.509 10 km
💡 实战场景:
- 附近的人、门店搜索
- LBS 推荐、打车距离计算
- 物流轨迹存储
✅ 优势:
- 精度高(约几米到几公里)
- 查询高效
⚠️ 注意事项:
- 经纬度顺序为 longitude latitude
- 不支持海拔
8. HyperLogLog:海量数据去重计数
✅ 特性:
- 基数估算算法,误差 < 0.81%
- 固定内存(约 12KB),可统计上亿唯一值
🔧 常用命令:
PFADD stats:uv:2024-04-05 user1 user2 user3 user1
PFCOUNT stats:uv:2024-04-05 # 输出:3
# 合并多日 UV
PFADD stats:uv:week1 stats:uv:day1 stats:uv:day2
PFCOUNT stats:uv:week1
💡 实战场景:
- 网站独立访客(UV)统计
- 搜索词去重
- 广告曝光去重
⚠️ 注意事项:
- 是估算值,不适合精确计数
- 不能获取具体元素集合
9. Stream:强大的消息队列
✅ 特性:
- 持久化日志结构
- 支持消费者组、消息确认、回溯
🔧 常用命令:
# 添加消息
XADD mystream * name Alice action login
# 读取消息
XREAD COUNT 5 STREAMS mystream 0
# 创建消费者组
XGROUP CREATE mystream mygroup $ MKSTREAM
# 消费(带确认)
XREADGROUP GROUP mygroup consumer1 STREAMS mystream >
# 确认处理完成
XACK mystream mygroup msg_id
💡 实战场景:
- 日志收集
- 异步任务调度
- 微服务间通信
- 事件溯源(Event Sourcing)
✅ 优势:
- 消息不丢失(持久化)
- 支持多消费者组(广播模式)
- 可回溯历史消息
⚠️ 注意事项:
- 需定期裁剪(XTRIM)防止无限增长
- 消费者需手动确认(避免消息丢失)
三、选择合适数据类型的决策树
缓存对象 | String(JSON) 或 Hash |
计数器 | String(INCR) |
分布式锁 | String(SETNX) |
最新动态 | List 或 Stream |
排行榜 | Sorted Set |
标签/好友 | Set |
签到系统 | Bitmap |
附近的人 | Geo |
UV 统计 | HyperLogLog |
消息队列 | Stream(生产环境)或 List(简单场景) |
四、性能优化与最佳实践
避免大 key:
- 单个 key > 10KB 视为大 key
- 大 Hash/List/Sorted Set 建议分片(如 user:1001:orders:1)
使用 Pipeline 批量操作:
pipe = redis.pipeline()
pipe.set("a", 1)
pipe.set("b", 2)
pipe.execute()
合理设置 TTL:防止内存泄漏
监控大 key 和热 key:
- 使用 redis-cli –bigkeys
- 监控 info memory 和慢查询日志
使用 Lua 脚本保证原子性:
- 复杂逻辑在服务端执行,减少网络往返
五、总结:Redis 数据类型的“能力地图”
String | 缓存、计数 | 否 | 否 | 否 |
Hash | 对象存储 | 否 | field 唯一 | 否 |
List | 队列、栈 | 是 | 否 | 支持索引访问 |
Set | 去重、集合运算 | 否 | 是 | 否 |
Sorted Set | 排行榜、延迟队列 | 是(score) | 是 | ✅ 支持范围 |
Bitmap | 布尔统计 | 是(bit 位) | 否 | ✅ 位运算 |
Geo | 地理位置 | 是(GeoHash) | 是 | ✅ 范围搜索 |
HyperLogLog | 去重计数 | 否 | 自动去重 | 否 |
Stream | 消息队列 | 是(ID) | 是 | ✅ 按 ID 范围 |
✅ 结语:
Redis 的“大数据类型”是其超越传统缓存的核心竞争力。掌握每种类型的特点与适用场景,能让你在架构设计中游刃有余,实现高性能、低延迟、高可用的系统。
记住:用对数据结构,比优化代码更重要。
评论前必须登录!
注册