Redis 数据类型全解(详细版)
适用版本:Redis 5/6/7 均可(个别特性在文中注明)。
目标:把 “有哪些类型 + 怎么用 + 复杂度 + 典型场景 + 坑” 一次讲透。
目录
- 1. 总览:Redis 核心数据类型
- 2. 通用基础:Key 设计、TTL、SCAN、序列化与大 Key
- 3. String(字符串)
- 4. Hash(哈希)
- 5. List(列表)
- 6. Set(集合)
- 7. ZSet(有序集合)
- 8. Bitmap(位图)
- 9. HyperLogLog(基数统计)
- 10. GEO(地理位置)
- 11. Stream(流)
- 12. 选型速查表
- 13. 线上常见坑与排查清单
- 14. 附:命令复杂度速记
1. 总览:Redis 核心数据类型
Redis 的 “类型” 可以分两层理解:
- Key 的逻辑类型(你写命令时感知到的)
- String、Hash、List、Set、ZSet、Bitmap、HyperLogLog、GEO、Stream
- 底层编码(encoding)(Redis 内部为了省内存/提性能会切换)
- 比如 Hash 可能用 listpack/hashtable;ZSet 可能用 listpack/skiplist;List 可能用 quicklist。
- 你一般不需要死记编码细节,但要知道:同一种逻辑类型在不同规模下性能/内存差异可能很大。
2. 通用基础:Key 设计、TTL、SCAN、序列化与大 Key
2.1 Key 命名规范(强烈建议)
- 业务前缀 + 模块 + 资源 + 主键 + 子资源
例:pay:order:10001im:conv:8899:msgsuser:profile:42
- 避免“全局无前缀”:防止冲突、难以扫库、难以迁移
- 可观测性:把“维度”放进 key,方便定位(但别把超长 JSON 塞进 key)
2.2 TTL 与过期策略
- 设置过期:
EXPIRE key seconds/SET key v EX seconds - 常见策略:
- 缓存:强制 TTL(避免永不过期)
- 会话:滑动过期(每次访问续期)
- Redis 过期是 惰性删除 + 定期抽样,所以过期 key 不一定瞬间消失。
2.3 扫描与遍历
KEYS pattern:生产慎用(可能阻塞)SCAN:渐进式遍历,适合线上运维脚本
2.4 序列化建议
- 小对象:JSON / MsgPack / Kryo 均可(看语言栈)
- 强建议在 value 里加
version字段:便于灰度升级与回滚 - 对于热路径:能用 Hash 拆字段 就别把一个大 JSON 反复读写
2.5 大 Key / 热 Key(线上杀手)
- 大 Key:单个 key 过大(比如一个 List 几百万元素、一个 Hash 几十万 field)
- 热 Key:访问集中导致单核打满
- 常用手段:
- 拆分:
key:{id}:part:{n} - 限制:写入前做 size guard
- 迁移:把超热 key 下沉到本地缓存/多副本/分片
- 拆分:
3. String(字符串)
3.1 适用场景
- 缓存对象(序列化后存)
- 计数器(点赞、访问量)
- 分布式锁(配合
SET NX EX) - Token / 验证码 / 短期状态
3.2 常用命令
- 读写:
GETSETMGETMSET - 递增:
INCRINCRBYDECRINCRBYFLOAT - 追加:
APPEND - 位操作(配合 Bitmap 也会用):
GETBITSETBITBITCOUNT
3.3 复杂度与注意点
GET/SET:O(1)INCR:O(1)(内部是整数编码)- 避免超大 value:单次网络与复制成本高;RDB/AOF 重写也更重
3.4 典型例子:计数器 + 过期窗口
# 统计某接口 1 分钟内请求次数
INCR api:rate:/v1/pay:minute:20251223T0912
EXPIRE api:rate:/v1/pay:minute:20251223T0912 120
4. Hash(哈希)
4.1 适用场景
- 对象/实体字段存储:用户资料、配置项
- 避免读写整块 JSON:只更新某些字段
- 小型聚合结构(field 数量可控)
4.2 常用命令
HSETHGETHMGETHGETALLHDELHINCRBYHINCRBYFLOATHLENHKEYSHVALS
4.3 复杂度与坑
HSET/HGET:O(1)HGETALL:O(n)(n 为 field 数)- 大 Hash 坑:
HGETALL一把梭可能把网卡打爆- 主从复制成本高
- 建议:field 规模上来就分页/按需读,或做拆分。
4.4 推荐 Key 结构
user:profile:{uid}-> Hash
fields:nameageavatarlevelupdateAt
5. List(列表)
5.1 适用场景
- 简单消息队列(不推荐做高可靠 MQ)
- 时间线/最近记录(LRANGE + 裁剪)
- 栈/队列:LPUSH/RPOP 等
5.2 常用命令
- 入队/出队:
LPUSHRPUSHLPOPRPOP - 阻塞队列:
BLPOPBRPOP - 范围查询:
LRANGE - 裁剪:
LTRIM - 长度:
LLEN
5.3 复杂度与坑
- 两端 push/pop:O(1)
LRANGE:O(start+len)(与返回元素数相关)- 坑:
- 列表过大导致内存与复制开销巨大
- 用 List 当 MQ:缺少消费确认、重试、死信等机制
- 真要做队列更建议:Stream 或 专业 MQ(RocketMQ/Kafka)
5.4 经典:最近 N 条记录
LPUSH feed:user:42 "msg1"
LPUSH feed:user:42 "msg2"
LTRIM feed:user:42 0 99 # 只保留最近 100 条
LRANGE feed:user:42 0 9 # 取最近 10 条
6. Set(集合)
6.1 适用场景
- 去重(UV、标签集合)
- 关系运算:交集/并集/差集
- 随机抽取:
SRANDMEMBER/SPOP
6.2 常用命令
- 增删查:
SADDSREMSISMEMBERSMEMBERS - 集合运算:
SINTERSUNIONSDIFF - 随机:
SRANDMEMBERSPOP - 基数:
SCARD
6.3 复杂度与坑
SADD/SISMEMBER:O(1)SMEMBERS:O(n)- 集合运算:O(n+m+…)(按参与集合规模)
- 坑:大集合运算一次性返回非常重,考虑
SINTERSTORE落地再分页读(或用 ZSet 做“可分页”集合)
7. ZSet(有序集合)
7.1 适用场景(最常用也最容易玩出花)
- 排行榜(分数 + 名次)
- 延时队列(score = 触发时间戳)
- 需要“可分页”的集合(按 score 或 rank 取范围)
- 近实时统计:topN、区间统计
7.2 常用命令
- 增改:
ZADDZINCRBY - 取排名:
ZRANKZREVRANK - 取范围(按 rank):
ZRANGEZREVRANGE - 取范围(按 score):
ZRANGEBYSCOREZREVRANGEBYSCORE - 删除:
ZREM - 统计:
ZCOUNT - 弹出:
ZPOPMINZPOPMAX(适合做“准队列”)
7.3 复杂度与坑
ZADD/ZREM:O(log n)ZRANGE:O(log n + m)(m 为返回元素数)- 延时队列坑:
- 需要循环扫描到期任务(轮询)
- 多消费者并发抢任务需原子性(Lua 脚本 /
ZPOPMIN+ 校验)
- score 精度:score 是 double;若用时间戳建议用毫秒整数存为 double(通常安全),更极端可把排序拆成
(score, member)组合避免碰撞。
7.4 延时队列(简单实现思路)
ZADD delay:jobs {runAtMillis} jobId- worker:
ZRANGEBYSCORE delay:jobs -inf now LIMIT 0 100- 原子抢占:Lua 里校验 score <= now 再
ZREM,抢到就处理
8. Bitmap(位图)
Bitmap 本质还是 String,只是用 bit 位做标记。
8.1 适用场景
- 签到/打卡(天维度)
- 活跃标记(某天是否登录)
- 用户画像的二值特征(低维场景)
8.2 常用命令
SETBIT key offset 0/1GETBITBITCOUNT(统计 1 的数量)BITOP(与/或/异或,做集合运算很猛)
8.3 典型:月签到
- key:
sign:2025-12:{uid} - offset:day-1(1 号对应 offset 0)
SETBIT sign:2025-12:42 22 1 # 12月23日签到
BITCOUNT sign:2025-12:42 # 统计本月签到次数
8.4 注意点
- offset 太大会导致 value 自动扩容(潜在大 Key)
- 不适合稀疏超大空间(比如 offset=10^9),那会炸内存
9. HyperLogLog(基数统计)
9.1 适用场景
- UV 统计(去重计数),允许小误差(标准误差大约 ~0.81% 左右)
- 海量去重计数但不关心“是谁”
9.2 常用命令
PFADD key element...PFCOUNT keyPFMERGE dest k1 k2 ...
9.3 优缺点
- 优点:非常省内存(通常 12KB 左右级别)
- 缺点:
- 有误差
- 不能列出具体成员(只给“数量”)
10. GEO(地理位置)
GEO 底层其实是 ZSet,把经纬度编码成 score。
10.1 适用场景
- 附近的人/店:按半径查找
- 地理围栏的粗粒度候选集(精确判断建议在业务层二次过滤)
10.2 常用命令
GEOADD key lon lat memberGEOPOS key memberGEODIST key m1 m2 kmGEOSEARCH key FROMLONLAT ... BYRADIUS ... WITHDIST WITHCOORD COUNT ...
(老版本常见 GEORADIUS 系列,Redis 6.2 之后更推荐 GEOSEARCH。)
10.3 注意点
- 地球是球体 + 投影误差:结果是近似
- 数据量大时要注意 key 分区(按城市/网格拆 key)
11. Stream(流)
Redis Stream = 内建“消息流/日志”结构,支持消费者组。
相比 List:支持消费确认、pending、重放,更像轻量 MQ。
11.1 适用场景
- 事件总线(订单状态变化、异步任务)
- 日志/埋点流式消费
- 轻量级队列(不想上 Kafka/RocketMQ 的情况下)
11.2 核心概念
- 消息 ID:形如
1690000000000-0 - 消费者组(Consumer Group):
XGROUP CREATEXREADGROUP读取并把消息分配给某个 consumer
- Pending(未确认):
- 消费者读走但未
XACK的消息会在 PEL(Pending Entries List)里 - 可用
XPENDING查看、XCLAIM夺回“死掉消费者”的消息
- 消费者读走但未
11.3 常用命令
- 追加消息:
XADD stream * field value ... - 创建组:
XGROUP CREATE stream group $ MKSTREAM - 组内消费:
XREADGROUP GROUP group c1 COUNT 10 BLOCK 2000 STREAMS stream > - 确认:
XACK stream group id... - 查看 pending:
XPENDING stream group - 夺回:
XCLAIM stream group c2 min-idle-time id...
11.4 典型消费流程(可靠)
- producer
XADD - consumer
XREADGROUP ... > - 处理成功
XACK - 处理失败:
- 不 ack(留在 pending)
- 后台任务定期
XPENDING+XCLAIM重试/转移
11.5 注意点
- Stream 会一直增长:要做裁剪
XTRIM stream MAXLEN ~ 100000(近似裁剪更省)
- 组内消息分配是“读者主动拉取”,不会自动 push
12. 选型速查表
| 需求 | 推荐类型 | 关键理由 |
|---|---|---|
| 缓存一个对象 | String / Hash | 小对象 String 简单;字段更新多用 Hash |
| 自增计数 | String | INCR 原子 O(1) |
| 关注/粉丝、标签集合 | Set | 去重与集合运算 |
| 排行榜、topN、延时队列 | ZSet | score 排序 + 范围查询 |
| 最近 N 条记录 | List | 两端操作 O(1) + LTRIM |
| 签到/活跃 | Bitmap | 位级存储超省 |
| UV(允许误差) | HyperLogLog | 内存极省 |
| 附近门店 | GEO | 半径搜索 |
| 可靠队列/事件流 | Stream | consumer group + ack + pending |
13. 线上常见坑与排查清单
13.1 命令时间突然变长
- 是否有
KEYS/HGETALL/SMEMBERS/ 大范围ZRANGE? - 是否出现大 Key 导致单次返回巨大?
- 是否 AOF rewrite / RDB save 触发 IO 抖动?
排查命令:
SLOWLOG GET 20INFO commandstatsMEMORY USAGE keySCAN+TYPE+LLEN/HLEN/SCARD/ZCARD
13.2 内存飙升
- 大 Key / 热 Key
- TTL 设置不合理(大量永不过期)
- 误用 Bitmap offset(offset 极大导致稀疏扩容)
- Stream 不裁剪,持续增长
排查命令:
INFO memoryMEMORY STATSMEMORY DOCTORMEMORY USAGE key
13.3 缓存击穿/雪崩
- 击穿:热点 key 过期瞬间大量回源
- 方案:互斥锁、逻辑过期、提前刷新
- 雪崩:大量 key 同时过期
- 方案:TTL 加随机抖动、分批预热
13.4 分布式锁误用(String)
正确姿势(最小可用):
SET lock:key token NX EX 10- 解锁用 Lua:校验 token 再 DEL(避免误删别人的锁)
14. 附:命令复杂度速记
n = 元素数量,m = 返回结果数量
- String:
GET/SET/INCRO(1) - Hash:
HGET/HSETO(1),HGETALLO(n) - List:两端
LPUSH/RPOPO(1),LRANGE~ O(m) - Set:
SADD/SISMEMBERO(1),SMEMBERSO(n),集合运算 ~ O(n+m) - ZSet:
ZADD/ZREMO(log n),ZRANGEO(log n + m) - Stream:
XADD均摊 O(1),读写与 pending/claim 取决于条目规模
一句话总结(选型脑回路)
- 只存一个值/计数:String
- 对象字段:Hash
- 队列/时间线:List(轻量)/ Stream(可靠)
- 去重集合:Set
- 排序/排行/定时任务:ZSet
- 二值状态:Bitmap
- UV 估算:HyperLogLog
- 附近:GEO
829

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



