2C4G 小机器上跑 Redis,内存是怎么省出来的
公司内部工具配的是 2C4G 的小机器,Redis 和应用混部,给 Redis 的只有 1.5GB。一开始半年没事,后来 key 数量涨到三百万开始频繁 OOM。这轮治理把内存压回 900MB,方法记录一下。
第一刀,编码阈值。Redis 对小数据有紧凑编码:Hash 用 listpack(7.0 之前是 ziplist)、Set 用 intset、ZSet 用 listpack。超过 *max-*-entries 或 *max-*-value 阈值就转标准结构,转换是不可逆的。我们的监控数据 Hash 平时只有几十个字段,但偶发大任务会塞进上千个字段,一旦转换就再也回不去。把 hash-max-listpack-entries 从 512 提到 1280,转化率直接归零。
第二刀,过期策略。检查发现大量 key 没设 TTL,是历史任务留下的。全量扫描加过期后,内存立刻降了 20%。顺手把 maxmemory-policy 从 noeviction 改成 volatile-lru——内部工具丢点冷数据没关系,OOM 重启才致命。
第三刀,碎片率。mem_fragmentation_ratio 长期 1.6,说明 40% 内存碎在了分配器手里。4.0 之后开 activedefrag yes,碎片率降到 1.1。低版本没法主动整理,只能低峰期 MEMORY PURGE 或者重启。
第四刀,大 key。redis-cli --bigkeys 扫出几个几十 MB 的 String(存的还是序列化后的整个列表对象),改成 Hash 分片后单个 key 控制在 1MB 内。大 key 除了占内存,还会造成删除时的阻塞,这也是为什么要用 UNLINK 而不是 DEL。
治理完的感悟:小机器上跑 Redis,收益最大的往往不是什么高级特性,而是这些"工程卫生"。