Skip to content

如果发现 Redis 内存溢出了?你会怎么做?请给出排查思路和解决方案 ​

  • 标签:后端、场景题

🏛 业务架构师 ​

我的排查 SOP 分止血、定位、根治三段:

第一步:止血(先恢复业务,再查根因)

  • 降级非核心的 Redis 使用方(通知、统计类切走或关闭);
  • 紧急扩容(云上垂直升配最快)或主从切换;
  • 保交易主链路:核心缓存走本地缓存兜底/限流保护 DB。

第二步:定位(数据去哪了)

  1. info memory:used_memory 与 used_memory_rss、碎片率——碎片高(>1.5)是回收失衡,重启或 activedefrag;
  2. 过期堆积:expires 与总 key 数对比——惰性删除跟不上过期速度(批量短 TTL key 场景),大量"逻辑过期物理未删";
  3. bigkey 扫描:redis-cli --bigkeys / RDB 离线分析——一个 500MB 的大集合是内存溢出的头号常客;
  4. 淘汰策略与 maxmemory 检查:noeviction 下写报错但内存仍在涨?策略是否符合数据分级;
  5. 增长曲线对齐业务事件:某次上线/活动后开始涨 = 新代码引入的泄漏(缓存只写不清、TTL 没设)。

第三步:根治与预防

  • bigkey 拆分/结构调整;TTL 全覆盖审计(没 TTL 的 key 要白名单报备);
  • 冷数据下线归档;
  • 机制化:内存水位告警(70% 警告/85% 动作)、周期巡检 bigkey、新缓存需求评审过"增长模型 + 过期策略 + 丢失影响"三问;
  • 大促前容量压测与扩容预案。

业务视角总结:内存溢出几乎都是**"缓存无纪律"的积累**——写得爽、不管死。治理的关键是把"每个 key 的生命周期都有 owner"变成团队规范,架构师的责任是让制度跑在事故前面。

🏗 系统架构师 ​

Redis 内存溢出(达到 maxmemory 后写入报 OOM,或机器级内存耗尽)我按"排查 → 应急 → 根治"三段答:

1. 排查定位:

  • INFO memory:used_memory vs maxmemory、mem_fragmentation_ratio(碎片率 > 1.5 说明碎片严重,频繁写删小对象所致;< 1 可能已用 swap,最危险);
  • INFO keyspace:各库 key 数量与 TTL 覆盖率——大量无 TTL 的 key 是头号嫌疑;
  • redis-cli --bigkeys / RDB 离线分析定位 big key;
  • MEMORY USAGE key 精确单 key;
  • 客户端连接数与缓冲区(client-output-buffer,慢消费者堆积)。

2. 应急止血:

  • 临时调大 maxmemory(有物理余量时);
  • 淘汰策略确认:缓存场景确保是 allkeys-lru/lfu 而非默认 noeviction;
  • 低峰清理明确的垃圾 key(分批 UNLINK,禁止 FLUSHALL);
  • 杀掉大 key 来源的写入(某些任务在灌脏数据)。

3. 根治:

  • big key 拆分压缩;无 TTL 的 key 补过期策略;持久化 fork 内存翻倍预留(maxmemory 设物理内存 50~60%);
  • 容量规划:监控 used_memory 增速,水位 70% 告警,到 80% 扩容/分片(迁移用 SCAN 双写切流);
  • activedefrag 碎片整理(4.0+,低峰开启)。

预防机制:内存水位是 Redis 最核心的容量指标,我的规范里 big key 扫描(周任务)+ TTL 覆盖率巡检 + 增速告警是三件必配。

💻 全栈开发专家 ​

先稳住,再排查,按"数据面 → 配置面 → 架构面"走:

第一步:确认现象和止血

bash
info memory     # used_memory vs maxmemory;fragmentation ratio
  • 已经 OOM 拒写:立即评估能否临时扩容 maxmemory(有物理余量时)或清理明确可丢的 key;
  • 核心原则:不能让 Redis 故障传导成 DB 打穿——客户端限流/熔断先开起来。

第二步:定位内存去向(四个常见元凶)

  1. bigkey:redis-cli --bigkeys + MEMORY USAGE;找到就拆分/删除(UNLINK);
  2. 没设 TTL 的缓存:redis-cli --extended-describe... 实际用扫描脚本统计无过期 key 的量和类型(业务 bug 常见:SET 覆盖丢了 TTL);
  3. 淘汰策略不对:noeviction + 内存满 = 拒写;纯缓存实例改 allkeys-lfu,CONFIG SET 即时生效;
  4. 内存碎片:mem_fragmentation_ratio > 1.5 说明碎片严重(频繁修改不同大小 value),开 activedefrag 或重启整理。

第三步:治本

  • 容量规划:峰值数据 × 2 倍冗余;内存使用率 70% 告警;
  • 架构:单实例内存超 10GB 就该拆实例/上 Cluster(fork 停顿和故障影响面都小);
  • 流程:bigkey 巡检、TTL 规范进上线 checklist。

真实案例:我处理过一次"本地缓存迁移后忘删旧 key",8GB 实例 90% 是 3 个月前的僵尸数据——SCAN + 按前缀批量 UNLINK,两小时清完,比扩容便宜得多。