跳转到内容

数据结构与内部编码

用途:选型“这个数据该用什么结构存”、解释内存占用异常

类型 常用内部编码 说明
String int / embstr(≤44B)/ raw 短字符串共享对象头,省内存
Hash listpack(小)/ hashtable 元素少且短用紧凑编码,超过阈值转换
List listpack / quicklist(双向链表 + listpack 节点) 兼顾插入与内存
Set intset / hashtable 整数集合紧凑存储
ZSet listpack / skiplist + hashtable 跳表支持范围查询,哈希支持按成员取分值
Stream radix tree(listpack 节点) 追加型日志结构,支持消费者组
特殊 HyperLogLog、Bitmap、GeoSet、Module 类型 HLL 是概率结构,有误差率

阈值参数:hash-max-listpack-entries/value、zset-max-listpack-* 等。编码转换单向(转成大结构后不会自动转回),因此设计 key 时要控制单 key 元素数量。

需求 结构 要点
对象缓存 String(序列化 JSON) 注意大 value 与序列化开销
部分字段更新 Hash 可 HINCRBY、HGET 单字段,避免整体读写
排行榜 / 延时队列 ZSet 分值排序,ZRANGEBYSCORE 取区间
去重集合 / 标签 Set SINTER/SDIFF 做交并差
计数 / 唯一性判断 String + INCR / Set 注意过期与分片计数
概率去重统计 HyperLogLog 12KB 级别估算基数,误差约 0.81%
布尔状态位图 Bitmap 签到、活跃标记,BITCOUNT 聚合
消息流与消费位点 Stream 有 ID、可回溯、支持消费者组与 ACK
分布式限流 ZSet(滑动窗口)或 String + Lua 原子性用 Lua 保证

INFO memory 与 MEMORY USAGE key、MEMORY DOCTOR、--bigkeys / --hotkeys 是排查入口。常见放大来源:

  1. key 本身占内存:冗长前缀、把业务字段拼进 key。
  2. 编码转换:Hash 元素超过阈值转 hashtable,指针开销显著。
  3. 过期数据未回收:惰性删除 + 定期采样删除,大量 key 同时过期会造成内存不降与延迟毛刺(需要 active-expire-effort 或分批过期)。
  4. 客户端输出缓冲区:大 key 批量读取会撑高 mem_clients_normal。
  5. 碎片率:mem_fragmentation_ratio 高(>1.5)时考虑重启整理或 activedefrag。
  • 单 key 元素数上限(经验:集合类 < 5000,value < 10KB),大 key 会放大网络、序列化与迁移代价。
  • key 命名分层:业务:域:标识,便于扫描、统计与 TTL 治理。
  • 必须设 TTL:无过期时间的 key 是内存泄漏的主要来源;缓存类尤其如此。
  • 热点结构分片:单个高并发计数 key 拆成 N 个(counter:{shard})读时求和。
误区 修正
用 KEYS * 找 key 阻塞主线程,用 SCAN 游标
把 Redis 当持久化数据库 语义与可靠性模型不匹配,需 DB 兜底
大 Hash 当“表”存百万字段 单 key 巨大,迁移与删除代价高,应拆分
只监控命中率不监控大 key/碎片 故障常来自内存与延迟,不是命中率
以为 DEL 立即释放内存 大 key 删除阻塞,用 UNLINK 异步释放
  • Redis 官方文档:Data types、Internal encoding、Memory optimization
  • 《Redis 设计与实现》(黄健宏)数据结构章节