Redis数据类型详解

Redis 数据类型全解(详细版)

适用版本:Redis 5/6/7 均可(个别特性在文中注明)。
目标:把 “有哪些类型 + 怎么用 + 复杂度 + 典型场景 + 坑” 一次讲透。


目录


1. 总览:Redis 核心数据类型

Redis 的 “类型” 可以分两层理解:

  1. Key 的逻辑类型(你写命令时感知到的)
  • String、Hash、List、Set、ZSet、Bitmap、HyperLogLog、GEO、Stream
  1. 底层编码(encoding)(Redis 内部为了省内存/提性能会切换)
  • 比如 Hash 可能用 listpack/hashtable;ZSet 可能用 listpack/skiplist;List 可能用 quicklist。
  • 你一般不需要死记编码细节,但要知道:同一种逻辑类型在不同规模下性能/内存差异可能很大

2. 通用基础:Key 设计、TTL、SCAN、序列化与大 Key

2.1 Key 命名规范(强烈建议)

  • 业务前缀 + 模块 + 资源 + 主键 + 子资源
    例:
    • pay:order:10001
    • im:conv:8899:msgs
    • user: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 常用命令

  • 读写:GET SET MGET MSET
  • 递增:INCR INCRBY DECR INCRBYFLOAT
  • 追加:APPEND
  • 位操作(配合 Bitmap 也会用):GETBIT SETBIT BITCOUNT

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 常用命令

  • HSET HGET HMGET HGETALL HDEL
  • HINCRBY HINCRBYFLOAT
  • HLEN HKEYS HVALS

4.3 复杂度与坑

  • HSET/HGET:O(1)
  • HGETALL:O(n)(n 为 field 数)
  • 大 Hash 坑
    • HGETALL 一把梭可能把网卡打爆
    • 主从复制成本高
  • 建议:field 规模上来就分页/按需读,或做拆分。

4.4 推荐 Key 结构

  • user:profile:{uid} -> Hash
    fields:name age avatar level updateAt

5. List(列表)

5.1 适用场景

  • 简单消息队列(不推荐做高可靠 MQ)
  • 时间线/最近记录(LRANGE + 裁剪)
  • 栈/队列:LPUSH/RPOP 等

5.2 常用命令

  • 入队/出队:LPUSH RPUSH LPOP RPOP
  • 阻塞队列:BLPOP BRPOP
  • 范围查询: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 常用命令

  • 增删查:SADD SREM SISMEMBER SMEMBERS
  • 集合运算:SINTER SUNION SDIFF
  • 随机:SRANDMEMBER SPOP
  • 基数: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 常用命令

  • 增改:ZADD ZINCRBY
  • 取排名:ZRANK ZREVRANK
  • 取范围(按 rank):ZRANGE ZREVRANGE
  • 取范围(按 score):ZRANGEBYSCORE ZREVRANGEBYSCORE
  • 删除:ZREM
  • 统计:ZCOUNT
  • 弹出:ZPOPMIN ZPOPMAX(适合做“准队列”)

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/1
  • GETBIT
  • BITCOUNT(统计 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 key
  • PFMERGE dest k1 k2 ...

9.3 优缺点

  • 优点:非常省内存(通常 12KB 左右级别)
  • 缺点:
    • 有误差
    • 不能列出具体成员(只给“数量”)

10. GEO(地理位置)

GEO 底层其实是 ZSet,把经纬度编码成 score。

10.1 适用场景

  • 附近的人/店:按半径查找
  • 地理围栏的粗粒度候选集(精确判断建议在业务层二次过滤)

10.2 常用命令

  • GEOADD key lon lat member
  • GEOPOS key member
  • GEODIST key m1 m2 km
  • GEOSEARCH 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 CREATE
    • XREADGROUP 读取并把消息分配给某个 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 典型消费流程(可靠)

  1. producer XADD
  2. consumer XREADGROUP ... >
  3. 处理成功 XACK
  4. 处理失败:
    • 不 ack(留在 pending)
    • 后台任务定期 XPENDING + XCLAIM 重试/转移

11.5 注意点

  • Stream 会一直增长:要做裁剪
    • XTRIM stream MAXLEN ~ 100000(近似裁剪更省)
  • 组内消息分配是“读者主动拉取”,不会自动 push

12. 选型速查表

需求推荐类型关键理由
缓存一个对象String / Hash小对象 String 简单;字段更新多用 Hash
自增计数StringINCR 原子 O(1)
关注/粉丝、标签集合Set去重与集合运算
排行榜、topN、延时队列ZSetscore 排序 + 范围查询
最近 N 条记录List两端操作 O(1) + LTRIM
签到/活跃Bitmap位级存储超省
UV(允许误差)HyperLogLog内存极省
附近门店GEO半径搜索
可靠队列/事件流Streamconsumer group + ack + pending

13. 线上常见坑与排查清单

13.1 命令时间突然变长

  • 是否有 KEYS / HGETALL / SMEMBERS / 大范围 ZRANGE
  • 是否出现大 Key 导致单次返回巨大?
  • 是否 AOF rewrite / RDB save 触发 IO 抖动?

排查命令:

  • SLOWLOG GET 20
  • INFO commandstats
  • MEMORY USAGE key
  • SCAN + TYPE + LLEN/HLEN/SCARD/ZCARD

13.2 内存飙升

  • 大 Key / 热 Key
  • TTL 设置不合理(大量永不过期)
  • 误用 Bitmap offset(offset 极大导致稀疏扩容)
  • Stream 不裁剪,持续增长

排查命令:

  • INFO memory
  • MEMORY STATS
  • MEMORY DOCTOR
  • MEMORY 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/INCR O(1)
  • Hash:HGET/HSET O(1),HGETALL O(n)
  • List:两端 LPUSH/RPOP O(1),LRANGE ~ O(m)
  • Set:SADD/SISMEMBER O(1),SMEMBERS O(n),集合运算 ~ O(n+m)
  • ZSet:ZADD/ZREM O(log n),ZRANGE O(log n + m)
  • Stream:XADD 均摊 O(1),读写与 pending/claim 取决于条目规模

一句话总结(选型脑回路)

  • 只存一个值/计数:String
  • 对象字段:Hash
  • 队列/时间线:List(轻量)/ Stream(可靠)
  • 去重集合:Set
  • 排序/排行/定时任务:ZSet
  • 二值状态:Bitmap
  • UV 估算:HyperLogLog
  • 附近:GEO
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值