Redis内存溢出怎么排查和解决方法

文章导读
Redis 报内存溢出时,先不要急着改 maxmemory 或扩内存。多数情况是某一层先出了问题:数据写入速度超过淘汰速度、过期策略没生效、单个大 key 长期不释放,或者只是客户端看到的报错被误判成内存不够。排查顺序通常是先定位问题在哪一层,再看日志、命令输出、配置项和面板字段,然后按成因可能性从高到低排除,最后用命令确认修改有效并准备回滚。
📋 目录
  1. 一 Redis内存溢出先定位在哪一层
  2. 二 Redis内存溢出:先看这四个位置
  3. 三 Redis内存溢出:按可能性从高到低排除
  4. 四 Redis内存溢出:改完怎么确认真的解决了
A A

Redis 报内存溢出时,先不要急着改 maxmemory 或扩内存。多数情况是某一层先出了问题:数据写入速度超过淘汰速度、过期策略没生效、单个大 key 长期不释放,或者只是客户端看到的报错被误判成内存不够。排查顺序通常是先定位问题在哪一层,再看日志、命令输出、配置项和面板字段,然后按成因可能性从高到低排除,最后用命令确认修改有效并准备回滚。

Redis 内存溢出通常需要按“定位层 → 看四个位置 → 按概率排除 → 验证回滚”的顺序处理。判断依据是 INFO memory 里的 used_memory、maxmemory、evicted_keys,以及慢查询和客户端写入量。不建议第一时间放宽 maxmemory,应先确认是可淘汰数据增长还是不可淘汰数据堆积;内存上限、淘汰策略和过期时间的取值需要结合业务写入量与实例规格确认。

Redis内存溢出先定位在哪一层

Redis 的“内存溢出”至少有四种表现:OOM command not allowed when used memory > 'maxmemory'、写入失败但读正常、实例被系统 OOM Killer 杀掉、客户端连接超时。它们对应的层不一样,改错地方容易反复。

下面按层给出检查位置和判断依据:

  • Redis 进程层:执行 redis-cli -h 主机 -p 端口 INFO memory,看 used_memory 与 maxmemory 的关系。如果 used_memory 接近 maxmemory 且 evicted_keys 不增长,说明淘汰没有生效。
  • 键空间层:执行 redis-cli -h 主机 -p 端口 DBSIZE,再配合 SCAN 抽样看大 key。如果键数量稳定但内存持续涨,重点查大 key 和未设置 TTL 的键。
  • 客户端层:检查应用侧是否在循环写入、批量写入没有分批、或者缓存 key 没有设置过期时间。可以用 redis-cli MONITOR 短时间观察写入模式,但不要在高负载实例上长时间开着。
  • 系统层:在宿主机执行 free -m 和 dmesg -T | grep -i oom,确认 Redis 是否被系统 OOM Killer 终止过。如果 Redis 进程被杀,INFO memory 往往看不到溢出记录,需要从系统日志反推。

判断原则是:Redis 自己报 OOM 命令错误,优先看 maxmemory 和淘汰策略;Redis 进程消失或被重启,优先看系统内存和 OOM Killer;写入慢但不断报错,优先看客户端写入模式和大 key。

Redis内存溢出:先看这四个位置

定位到大致层之后,按下面四个位置拿证据。命令里的主机、端口、密码占位需要换成实际环境值,密码建议用 REDISCLI_AUTH 环境变量传入,避免出现在命令历史里。

1. Redis 日志

先在配置文件里确认 logfile 路径,或者用 redis-cli CONFIG GET logfile 查当前路径。常见位置是 /var/log/redis/redis-server.log 或 /var/log/redis/redis.log,需要以实际配置为准。查看命令:

tail -n 200 /var/log/redis/redis-server.log
# 关注包含 OOM、maxmemory、evict、fork、Background 的行
grep -iE 'oom|maxmemory|evict|out of memory' /var/log/redis/redis-server.log

日志里如果出现 OOM command not allowed,说明 Redis 自身已经达到上限;如果出现 Background save fork failed,则可能是持久化 fork 时内存翻倍导致的问题,和单纯的键值增长不是一回事。

Redis内存溢出怎么排查和解决方法

2. 命令输出

用 INFO memory 一次拿到关键字段,不要只看一个总数:

redis-cli -h 127.0.0.1 -p 6379 INFO memory | grep -E 'used_memory_human|used_memory_peak_human|maxmemory_human|maxmemory_policy|mem_fragmentation_ratio|evicted_keys'

再查淘汰和过期情况:

redis-cli -h 127.0.0.1 -p 6379 INFO stats | grep -E 'evicted_keys|expired_keys|keyspace_hits|keyspace_misses'

mem_fragmentation_ratio 明显大于 1 时,内存碎片占了一部分;used_memory_peak_human 远高于当前值,说明曾经有过峰值,需要结合业务高峰判断。

3. 配置项

把实际生效的配置查出来,而不是只看配置文件:

redis-cli -h 127.0.0.1 -p 6379 CONFIG GET maxmemory
redis-cli -h 127.0.0.1 -p 6379 CONFIG GET maxmemory-policy
redis-cli -h 127.0.0.1 -p 6379 CONFIG GET maxmemory-samples

maxmemory 为 0 表示不限制,此时如果报 OOM,问题通常来自系统内存或客户端,不是 Redis 自身上限;maxmemory-policy 为 noeviction 时,达到上限后写入会直接失败,这是排查中最常见的配置原因之一。

4. 监控面板字段

如果使用云厂商 Redis 或自建 Prometheus 监控,先确认面板上这几个字段的实际含义:内存使用量、内存使用率、被淘汰 key 数、过期 key 数、连接数、每秒写入量。不同面板对“内存使用率”的算法可能不同,有的按 used_memory 算,有的按系统 RSS 算,需要看面板说明或指标定义。比较稳妥的做法是用 INFO memory 的输出与面板对照一次,确认两者是否一致。

Redis内存溢出怎么排查和解决方法

Redis内存溢出:按可能性从高到低排除

下面按实际排查中出现频率从高到低排列。每个成因后面给出确认动作和处理方向,确认不了就先不要改配置。

  1. 淘汰策略为 noeviction 或没有设置过期时间。确认动作:redis-cli CONFIG GET maxmemory-policy,以及用 SCAN 0 COUNT 1000 抽样配合 TTL key 看键是否有过期时间。处理方向:根据业务能否丢数据选择 allkeys-lru 或 volatile-lru,并为缓存键补上 TTL。不能淘汰的数据不要和可淘汰缓存放在同一实例。
  2. 大 key 或热 key 长期占用内存。确认动作:redis-cli --bigkeys -h 127.0.0.1 -p 6379,或在从节点上执行,避免影响主节点。处理方向:拆分大 hash、大 list,控制单个 value 大小;已经写入的历史大 key 需要评估删除或迁移的影响,不要直接在业务高峰执行 DEL 大 key,可以用 UNLINK 异步删除。
  3. 写入量突增或缓存击穿后全量回填。确认动作:观察 INFO stats 里的 total_commands_processed 增长速率,或用 MONITOR 短时间采样写入命令类型。处理方向:在应用侧加分批写入、限制并发回填,必要时在缓存层加短 TTL 防止同一批 key 同时过期。
  4. 持久化 fork 导致内存峰值翻倍。确认动作:查看日志中 Background saving started 与失败记录,对比 used_memory_peak_human 和当前值。处理方向:控制实例内存不超过系统可用内存的一半左右,或将持久化放到从节点,具体比例需要结合写入放大和系统内存确认。
  5. 内存碎片或客户端缓冲区占用。确认动作:看 INFO memory 中的 mem_fragmentation_ratio 和 INFO clients 中的 client_recent_max_input_buffer、client_recent_max_output_buffer。处理方向:碎片持续偏高时可评估 activedefrag,客户端缓冲区异常时检查是否有慢客户端或大范围 KEYS、SMEMBERS 类命令。

处理时优先动配置和写入侧,不要一上来就扩内存。扩内存能暂时缓解,但如果成因是大 key 或不设过期时间,扩容后仍会再次溢出。

Redis内存溢出:改完怎么确认真的解决了

修改之后需要从命令输出和监控两个方向确认。下面这组命令可以每隔一段时间执行一次,对比数值变化:

redis-cli -h 127.0.0.1 -p 6379 INFO memory | grep -E 'used_memory_human|maxmemory_human|mem_fragmentation_ratio'
redis-cli -h 127.0.0.1 -p 6379 INFO stats | grep -E 'evicted_keys|expired_keys'
redis-cli -h 127.0.0.1 -p 6379 DBSIZE

判断稳定的依据通常是:used_memory 在业务高峰后能回落或保持平稳,evicted_keys 不再持续快速增长,应用侧不再出现 OOM 写入报错。观察时长建议至少覆盖一个完整业务高峰周期,比如一个工作日加上夜间低谷;如果业务有明显周期性,最好跨过一个高峰再判断。没有覆盖高峰就宣布解决,容易在下一个高峰复现。

回滚办法要提前准备好。以调整 maxmemory-policy 为例,修改前先记录原值:

# 修改前记录
redis-cli CONFIG GET maxmemory-policy > /tmp/redis-maxmemory-policy.bak

# 修改
redis-cli CONFIG SET maxmemory-policy allkeys-lru

# 回滚
redis-cli CONFIG SET maxmemory-policy "$(awk 'NR==2{print}' /tmp/redis-maxmemory-policy.bak)"

如果用 CONFIG SET 改的是运行中配置,重启后会回到配置文件里的值,需要同步修改配置文件并确认加载路径。回滚时同样用 CONFIG SET 改回原值,并用 CONFIG GET 确认生效。涉及删除大 key 的操作无法直接回滚,执行前应先确认 key 能否从源数据重建,或者先导出再删除。