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

7 min read
Table of Contents

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

Redis 内存溢出排查与解决方案

【核心定义】

Redis 内存溢出是指 Redis 实例使用的内存超过了其配置的最大内存限制(maxmemory),导致写入操作失败或触发内存淘汰策略,进而影响服务可用性。

【关键要点】

  1. 确认溢出现象:通过 INFO memory 命令查看 used_memory 是否接近或超过 maxmemory,并检查 used_memory_peak 的历史峰值。
  2. 分析内存组成:使用 MEMORY STATS 或 INFO memory 细分内存占用,重点关注 used_memory_dataset(数据占用)与 used_memory_overhead(管理开销)。
  3. 识别大Key:通过 redis-cli --bigkeys 或 MEMORY USAGE key 命令定位占用异常的大Key(如大String、大Hash、大List)。
  4. 检查客户端与副本:确认 client-output-buffer-limit 是否合理,避免客户端阻塞或副本同步缓冲区积压;检查 used_memory_slaves(副本缓冲区)。
  5. 评估淘汰策略:检查当前 maxmemory-policy 配置,确认淘汰策略是否生效及是否合理。

【深度推导/细节】

内存溢出的根本原因逻辑拆解

Step 1 – 数据增长超预期

  • 业务数据量自然增长,未及时调整 maxmemory。
  • 写入流量突增,如热点数据爆发或爬虫灌入。

Step 2 – 内存碎片过高

  • Redis 内存分配器(jemalloc)产生外部碎片,导致 used_memory_rss(物理内存)远大于 used_memory(逻辑内存)。
  • 频繁更新不同大小的键值对加剧碎片化,可通过 mem_fragmentation_ratio(= used_memory_rss / used_memory)判断(>1.5 需警惕)。

Step 3 – 缓冲区与开销失控

  • 客户端输出缓冲区积压(特别是发布订阅或慢查询时)。
  • AOF 重写或 RDB 保存时的写时复制(Copy-On-Write)内存峰值。
  • 大量连接(每个连接约 10KB)或大量键(每个键元数据约 96 字节)导致管理开销膨胀。

Step 4 – 淘汰策略失效

  • 设置为 noeviction 时,写入直接报错(OOM)。
  • 设置为 allkeys-lru 但所有键均无过期时间或访问模式均匀,LRU 近似算法失效。

【关联/对比】

  • Redis vs. Memcached:Redis 支持复杂数据结构与持久化,内存开销更大;Memcached 纯缓存,内存管理更简单。
  • 内存淘汰 vs. 过期删除:淘汰是主动移除键以释放空间(触发条件:used_memory > maxmemory);过期是被动或定期删除(基于 TTL)。
  • Redis 集群模式:集群模式下内存溢出可能仅发生在单个分片,需按分片诊断。

【解决方案】

应急处理

  1. 临时扩容:动态调整 maxmemory(需确保物理内存充足)。
  2. 强制淘汰:手动执行 FLUSHDB 或删除大Key(风险高,优先从副本操作)。
  3. 切换策略:将 maxmemory-policy 临时改为 allkeys-lru 或 volatile-lru 缓解压力。

根治措施

  1. 优化数据结构
    • 拆分大Key:将大Hash拆分为多个小Hash(通过哈希取模)。
    • 使用压缩:对长字符串启用 LZF 压缩(hash-max-ziplist-entries/value 等参数调优)。
  2. 控制内存开销
    • 设置合理 TTL,避免永久存储。
    • 限制客户端缓冲区:调整 client-output-buffer-limit。
    • 监控碎片率:定期重启或使用 MEMORY PURGE(仅支持 jemalloc ≥ 5.0)整理碎片。
  3. 架构升级
    • 启用集群模式,将数据分片到多个实例。
    • 使用 Redis 6.0 的客户端缓存(Client-side Caching)减少服务端存储。
  4. 完善监控
    • 设置 used_memory 告警阈值(如 80% of maxmemory)。
    • 定期采集 MEMORY STATS 与慢查询日志。

【版本差异】

  • Redis 4.0+:支持 MEMORY 命令族(USAGE、STATS、PURGE),提供更细粒度内存分析。
  • Redis 6.0+:支持多线程 I/O,缓解客户端缓冲区压力;引入 RESP3 协议优化内存效率。
  • Redis 7.0+:改进内存碎片整理,支持 jemalloc 主动碎片整理配置。

『面试官追问』

  1. 如何在不重启的情况下降低内存碎片率?

    • 启用 activedefrag 配置(Redis 4.0+),设置 active-defrag-ignore-bytes 与 active-defrag-threshold-lower 触发自动整理。
    • 手动执行 MEMORY PURGE(需 jemalloc 支持)。
  2. allkeys-lru 与 volatile-lru 在实际业务中如何选择?

    • 若所有数据均可淘汰,用 allkeys-lru。
    • 若部分数据必须持久化(如用户会话),为其设置 TTL 并使用 volatile-lru,无 TTL 键永不淘汰。
  3. Redis 内存溢出会影响持久化吗?

    • 会。AOF 重写需要写时复制内存,若 maxmemory 设置过低,重写可能因 OOM 失败。
    • 建议:maxmemory 留出 20%-30% 缓冲区供持久化操作。
  4. 如何预估合理的 maxmemory 值?

    • 公式:maxmemory = 物理内存 * 80% - 系统预留。
    • 基于业务数据量模拟压测,观察 used_memory_peak 并留出 30% 余量。
  5. 大Key除了内存问题还会引发什么风险?

    • 网络阻塞:单次传输耗时高。
    • 阻塞命令:DEL 大Key 会阻塞线程,建议用 UNLINK(异步删除)。
    • 数据倾斜:在集群中导致分片负载不均。
My avatar

文章看完了,不打算去别的篇章转转吗?👀 感谢你的耐心阅读!如果你有问题想探讨,或者发现了文中的 Bug,请务必通过页脚的链接联系我


More Posts

# 如何对 Java 的垃圾回收进行调优?

12 min read

Java 垃圾回收(GC)调优是通过调整 JVM 参数、选择合适的垃圾收集器以及优化应用程序代码,以达到特定性能目标(如低延迟、高吞吐量或最小内存占用)的系统性过程。

Read

Comments