结构选型的思路
Redis 的五种基础结构不是可以互换的存储容器,各自的访问模式不同:String 存单个值,Hash 存字段集合,List 是两端快插的序列,Set 是无序去重集合,ZSet 是按分数排序的集合,Stream 是带消费组的持久消息流。正确的选型顺序是先看读写模式(怎么访问),再看数据规模(多大、多热),最后才比较内存开销。用错结构的代价往往不是性能下降一点,而是某些操作根本无法实现。
基本信息
| 选型顺序 | 访问模式 → 数据规模与热度 → 内存与复杂度预算 |
|---|---|
| 编码确认 | OBJECT ENCODING 查看当前底层实现,MEMORY USAGE 估算占用 |
| 常见误区 | 用 List 做排序,用 ZSet 存全量日志,用 Hash 存海量字段 |
| 规模红线 | 单键元素过多会拖长单命令阻塞时间,需拆分或分页处理 |
结构选型速查
| 结构 | 典型场景 | 关键复杂度 | 内存特点 |
|---|---|---|---|
| String | 缓存对象、计数器、分布式锁 | GET/SET/INCR 均为 O(1) | 按内容长度,纯整数用 int 编码最省 |
| Hash | 用户属性、配置对象、按维度聚合计数 | HGET/HSET 为 O(1) | 小对象 listpack 紧凑,超阈值转 hashtable |
| List | 队列、时间线、最新 N 条 | 两端 O(1),中间 O(N) | quicklist 节点化存储 |
| Set | 标签、去重 ID、共同好友 | SADD/SISMEMBER 为 O(1),集合运算为 O(N) | 整数集合 intset 更省内存 |
| ZSet | 排行榜、延时队列、按权重的队列 | ZADD 与查询 O(log N) | 跳表加字典,开销高于 List 与 Set |
| Stream | 事件流、消息队列、审计日志 | XADD/XREAD 近似 O(1) | 按条目存储,必须用 MAXLEN 控制 |
按需求反查结构
- 要按名次或权重取数,选 ZSet;要按写入先后取数,选 List 或 Stream
- 要判断某个成员是否存在,选 Set 或 Hash;要统计存在数量,位图或 HyperLogLog 更省
- 要频繁更新对象的某一个字段,选 Hash;整对象读写一次到位,String 加序列化更简单
- 消息不能丢且需要重投与确认,选 Stream;只是实时广播且可容忍丢失,Pub/Sub 足够
- 数据量极大且只关心基数或存在性,优先考虑 Bitmap、HyperLogLog 这类近似或位级结构
快速确认编码与占用
快速确认编码与占用
redis-cli TYPE mykey # 逻辑类型
redis-cli OBJECT ENCODING mykey # 底层编码,判断是否仍是紧凑结构
redis-cli MEMORY USAGE mykey # 单键占用字节数
redis-cli TTL mykey # 是否设了过期时间
redis-cli --scan --pattern 'user:*' | head -n 5 # 抽样看键命名是否成体系内存评估不要只算数据本身:每个键都带字典条目与对象头的固定开销,几十字节的小键在数量上亿时,这部分开销可能超过数据本体。评估内存应按「键数量乘固定开销加数据体积」估算,并用 MEMORY USAGE 抽样校准。
编码会随数据增长变化:小集合用紧凑编码,超过阈值后转为常规结构,内存随之跳升。已经有数据的键不会因为后续删除元素而退回紧凑编码,因此编码升级点是容量规划必须考虑的因素。