ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Redis 内存优化全攻略

2026/8/25 22:17:28 拓冰建站 浏览量
Redis 内存优化全攻略

一、为什么 Redis「吃」内存?

  • Redis 所有数据常驻内存,每个对象不仅包含值本身,还包含类型、编码方式、引用计数等元数据
  • 默认使用 jemalloc 做内存分配;对象频繁创建 / 释放会导致 内存碎片,RSS(常驻集大小)与 used_memory 差距拉大。
  • 了解并善用 Redis 提供的多种“紧凑编码”与位级操作,可以节省 3~10 倍空间。([redis.io][1])

二、特殊编码:让小集合“瘦身”

数据类型Redis <= 6.2Redis 7.0+触发条件(元素数 / 元素长度)
Hashziplistlistpackhash-max-listpack-entries / hash-max-listpack-value(默认 512 / 64)
Setintset(全整数)→ listpack(7.2 新增)set-max-intset-entries / set-max-listpack-entries
ZSetziplist → listpackzset-max-listpack-entries / ...-value
  • 编码在小集合上自动生效,透明对业务;超过阈值即回退到哈希表 / 跳表。
  • 可通过 OBJECT ENCODING <key> 查看某键当前编码。([redis.io][1], [redis.io][2])

调参建议

# redis.conf
hash-max-listpack-entries 1024   # 如果字段很多但都很短,可适度提高
hash-max-listpack-value   128    # 值长度一般 < 128B
zset-max-listpack-entries 256

⚠️ 把阈值调得过大,会导致“胖集合→哈希表”转换耗时变长;上线前务必压测。

三、32 位实例:极致省内存的“小金刚”

  • make 32bit 编译可把指针宽度从 8 B 降到 4 B,每个键平均节省 16~24 B
  • 进程地址空间仅 4 GB,适合嵌入式 or 高密度容器场景。RDB/AOF 与 64 位实例兼容,可随时切换。

四、位 / 字节级操作:1 亿用户也只要 12 MB

SETBIT maillist:user 10000000 1   # 为第 10000000 位打标
BITCOUNT maillist:user            # 统计总订阅数
  • 位图方案非常节省空间:1 bit/用户,1 亿用户 ≈ 12 MB。
  • GETRANGE / SETRANGE 可把字符串当随机访问字节数组,用于稀疏计数器、布隆过滤器等。([redis.io][1])

五、用 Hash 模拟超轻量 KV

理念:与其创建 1 万个顶级 key,不如用“一个 Hash + 1 万个 field”。

5.1 普通对象与 Hash 的对比

# 方案 A:五个独立键
SET user:1:name  "Alice"
SET user:1:mail  "a@example.com"
...# 方案 B:一个 Hash
HSET user:1 name "Alice" mail "a@example.com"

在默认阈值下,10 个以内字段的 Hash 会使用 listpack/ziplist,元数据仅 ~4 B/field,远小于顶级 key 的几十字节。

5.2 “两级切片”技巧,10× 压缩普通 KV

当 Key 命名规则为 object:<id><id> 为自增数字时,可把 后两位作为 field,其余作为 Hash key

def split(key)               # object:1234head, tail = key.split(':'){ hash: "#{head}:#{tail[0..-3]}", field: tail[-2..] }  # object:12 , 34
end
  • 每个 Hash 固定 100 个 field(00~99),充分利用 listpack。
  • 作者实测 10 万条记录仅占 1.7 MB,而直接 SET 需要 11 MB。([redis.io][1])

六、maxmemory 与碎片治理

  1. 务必配置 maxmemory,否则 Redis 会一路吃光机器内存。

  2. 当键删除后,used_memory 下降但 RSS 不降

    • 归因于 jemalloc 不能把已分配的大页立即归还 OS。
    • 下一次写入会优先复用空闲块,不再增加 RSS。
  3. 监控指标

    • used_memory_rss / used_memory碎片率 > 1.5 时需关注。
    • Linux smem -rpss 观测真实占用。
  4. 定期重启(AOF 重写或 MEMORY PURGE)可释放碎片;生产 7×24 高可用场景请结合主从滚动重启。([redis.io][1])

七、一步步落地内存优化

步骤命令 / 工具目的
① 找大键redis-cli --bigkeysredis-cli --memkeys锁定“内存大户”
② 看编码OBJECT ENCODING k是否仍是 hashtable / skiplist
③ 升级小集合调低 ...-listpack-entries让更多键触发紧凑编码
④ 改数据模型Hash 聚合、小数值存 bitmap降维打击
⑤ 校验结果INFO memoryMEMORY STATS对比优化前后

八、生产最佳实践清单

  • 启用持久化(AOF everysec + RDB snapshot),防止 maxmemory-policy 逐出后找不到热数据。
  • 大批量插入前先 CONFIG SET hash-max-listpack-entries 心里有数;避免上线后阈值调高导致“批量解压”卡主线程。
  • 禁止 Lua/事务一次访问超大 Hash,否则会触发编码升级 + 阻塞。
  • 倾向选择 allkeys-lru / allkeys-lfu,并监控逐出速率;写 OOM 比业务慢更糟糕。
  • 碎片率持续 > 2 时,排查是否有“大键写入→删除”尖峰;必要时做冷重启。

九、总结

Redis 省内存 = 结构压缩 + 模型重构 + 参数调优 + 监控治理

  • 利用 listpack/intset 让“小集合占小钱”。
  • Hash 可以当“迷你数据库”用,合理拆分 key 能省一个数量级。
  • 位图 + 字节数组是处理超大布尔/计数集的王炸。
  • 控好 maxmemory、理解 jemalloc 行为,就不会被碎片“坑死”。

跟着本文逐条实践,你可以在 同配置机器上多存 3~10 倍数据,或在预算不变的情况下减轻节点压力、推迟扩容时机。祝各位内存一步到位,弹性再也不是难题!