noeviction策略下写操作直接报错是因为内存达maxmemory后硬性拒绝所有写命令,不释放key也不等待,仅允许读操作,导致“能读不能写”现象。

noeviction 触发后为什么写操作直接报错
noeviction 是 Redis 最保守的内存淘汰策略:当实例内存达到 maxmemory 后,所有写命令(如 SET、LPUSH、HSET)会立即返回 (error) OOM command not allowed when used memory > 'maxmemory'。它不尝试释放任何 key,也不阻塞等待,而是硬性拒绝——所以业务侧看到的是“服务突然不可写”,而非延迟升高。
这不是 bug,是策略设计使然。关键点在于:noeviction 不影响读命令(GET、HGETALL 等照常执行),所以你可能观察到“能读不能写”这种不对称现象。
- 检查是否真被触发:运行
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,它会提示是否存在bigkey、client-output-buffer过载、或slave缓冲区堆积等隐性内存占用,这些不会体现在used_memory里,但会挤占可用内存
常见陷阱:有人看到 used_memory_human: 980M、maxmemory_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_keys和evicted_keys对比判断是否处于“释放中抖动期” - 从节点缓冲区爆了:
redis-cli info replication | grep "slave[0-9]:"若看到state=online但lag持续为 0 或负数,说明从节点同步异常,主节点的repl-backlog和slaveoutput buffer 可能已吃光内存,此时需先修复主从链路
真正棘手的 case 往往不是策略选错,而是 maxmemory 和业务数据规模根本对不上,或者线上长期积累的 bigkey + 集中过期 + 客户端连接池泄漏,四者叠加压垮了内存水位线——这时候改配置只是把报错时间往后推了几分钟。










