登录
首页 >  数据库 >  Redis

Redis无淘汰策略引发服务中断排查指南

时间:2026-03-20 09:03:43 190浏览 收藏

本文深入剖析了Redis在启用noeviction内存淘汰策略时因内存达上限而突然拒绝所有写操作的典型故障现象,揭示其“能读不能写”的本质是策略设计的硬性保护机制而非系统异常;通过精准命令快速验证OOM事件、内存碎片、隐性内存占用(如客户端输出缓冲区、主从复制积压)等关键诱因,并提供分优先级的应急恢复路径——从清理临时数据、动态调高maxmemory、安全切换淘汰策略到终结异常连接;更直击常见误区,指出单纯修改策略却仍报错的三大深层原因:maxmemory设置严重不足、bigkey同步删除阻塞、从节点缓冲区耗尽,强调根治需回归容量规划与长期运维治理,而非仅依赖配置救火。

Redis如何排查noeviction导致的服务中断

noeviction 触发后为什么写操作直接报错

noeviction 是 Redis 最保守的内存淘汰策略:当实例内存达到 maxmemory 后,所有写命令(如 SETLPUSHHSET)会立即返回 (error) OOM command not allowed when used memory > 'maxmemory'。它不尝试释放任何 key,也不阻塞等待,而是硬性拒绝——所以业务侧看到的是“服务突然不可写”,而非延迟升高。

这不是 bug,是策略设计使然。关键点在于:noeviction 不影响读命令GETHGETALL 等照常执行),所以你可能观察到“能读不能写”这种不对称现象。

  • 检查是否真被触发:运行 redis-cli info memory | grep -E "(used_memory_human|maxmemory_human|mem_oom_events)",若 mem_oom_events 值在增长,说明已多次触发 OOM 拒绝
  • used_memory_human 接近甚至略超 maxmemory_human 是典型信号(注意:Redis 允许短暂小幅超限,但后续写入必拒)
  • 别只看 used_memory_human,还要对比 used_memory_peak_human:如果峰值远高于当前值,说明之前有过大 key 删除或批量过期,但内存未及时归还(可能因内存碎片或 lazy-free 未完成)

如何快速确认是不是 noeviction 在“背锅”

别一上来就改配置。先做三件事,5 分钟内定位根源:

  • 查当前策略:redis-cli config get maxmemory-policy —— 输出必须是 noeviction 才是它的问题
  • 查实时内存压力:redis-cli info memory | grep -E "used_memory|maxmemory|mem_fragmentation_ratio",若 mem_fragmentation_ratio > 1.5,说明内存碎片严重,即使 used_memory 没满,实际可分配空间也可能不足
  • 查有没有“假满”:redis-cli memory doctor,它会提示是否存在 bigkeyclient-output-buffer 过载、或 slave 缓冲区堆积等隐性内存占用,这些不会体现在 used_memory 里,但会挤占可用内存

常见陷阱:有人看到 used_memory_human: 980Mmaxmemory_human: 1G 就觉得“还有 20M 缓冲”,但忘了 client-output-buffer-limit slave 可能已占用 300M(尤其主从同步卡住时),真实可用内存早已为负。

临时恢复写入的实操路径

生产环境不能停,得边保服务边排查。以下操作按优先级排序,每步都可单独生效:

  • 立刻释放非核心内存:redis-cli eval "return redis.call('flushdb')" 0(仅限单库无重要数据场景);更安全的是用 redis-cli --scan --pattern "tmp:*" | xargs -r redis-cli del 清理临时前缀
  • 调高内存上限(需确保系统有余量):redis-cli config set maxmemory 2gb,注意:该设置重启失效,需同步改 /etc/redis/redis.conf 中的 maxmemory
  • 紧急切换淘汰策略(治标):redis-cli config set maxmemory-policy allkeys-lru,让 Redis 自动踢旧数据保写入;但要立刻跟进查 evicted_keys 是否突增,防止误删关键缓存
  • 检查客户端连接:redis-cli client list | grep -v "idle=0",找出空闲连接过多的客户端,它们的 output buffer 可能长期累积未清,用 redis-cli client kill id=xxx 主动断开

切记:config set 修改不持久,且某些策略(如 volatile-lfu)要求 Redis ≥ 4.0,线上版本不匹配会静默失败,执行后务必再 config get 确认。

为什么改了策略还是写不进去

改完 maxmemory-policy 却依然报 OOM,大概率掉进了这三个坑:

  • maxmemory 本身设得太小,比如只配了 128MB,而一个 zset 就占 150MB —— 淘汰策略再激进也救不了,因为没东西可淘汰(allkeys-* 类策略对单个超大 key 无效,它只能删 key,不能删 key 里的部分元素)
  • 存在阻塞型 bigkey 删除:比如刚执行了 DEL huge_list,Redis 正在同步释放内存,此时新写入仍会被拒绝,直到释放完成;可通过 redis-cli info stats | grep expired_keysevicted_keys 对比判断是否处于“释放中抖动期”
  • 从节点缓冲区爆了:redis-cli info replication | grep "slave[0-9]:" 若看到 state=onlinelag 持续为 0 或负数,说明从节点同步异常,主节点的 repl-backlogslave output buffer 可能已吃光内存,此时需先修复主从链路

真正棘手的 case 往往不是策略选错,而是 maxmemory 和业务数据规模根本对不上,或者线上长期积累的 bigkey + 集中过期 + 客户端连接池泄漏,四者叠加压垮了内存水位线——这时候改配置只是把报错时间往后推了几分钟。

今天关于《Redis无淘汰策略引发服务中断排查指南》的内容介绍就到此结束,如果有什么疑问或者建议,可以在golang学习网公众号下多多回复交流;文中若有不正之处,也希望回复留言以告知!

资料下载
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>