PUBLISH报OOM错误是因为其属写命令,当maxmemory达限且淘汰策略为noeviction时,Redis拒绝所有写入;根本原因是pubsub输出缓冲区未限制导致内存暴增,需配置client-output-buffer-limit并选用allkeys-lru等合适淘汰策略。

订阅发布失败不是因为 PUBSUB 命令本身被拒,而是内存满后 Redis 拒绝所有写入操作(包括 PUBLISH),此时必须启用并合理配置淘汰策略。
为什么 PUBLISH 会报 OOM 错误
Redis 的 PUBLISH 是写命令,触发内存分配(如复制消息到各订阅客户端的输出缓冲区)。当 used_memory ≥ maxmemory 且淘汰策略为默认的 noeviction 时,Redis 直接返回 (error) OOM command not allowed when used memory > 'maxmemory' —— 这和 SET 失败本质相同。
常见误判:以为 PUBSUB 是“只读”或“不占内存”,实际每个活跃订阅者都对应一个 client 输出缓冲区,大量订阅 + 高频发布会快速耗尽内存。
-
CONFIG GET maxmemory-policy返回noeviction就是根源 - 即使没用
SET,PUBLISH仍可能因缓冲区膨胀触发内存上限 -
redis-cli --stat中mem列持续接近maxmemory值是明确信号
选哪个淘汰策略能兼顾 PUBSUB 稳定性
不能随便选 allkeys-random 或 volatile-ttl:前者可能误删正在被消费者读取的缓存 key,后者对无 TTL 的 pubsub 状态数据无效。关键看你的数据是否有 TTL:
- 如果业务中绝大多数 key 都设置了过期时间(比如缓存结果、会话 token),选
volatile-lru—— 只动带 TTL 的 key,不影响持久化状态类数据 - 如果 key 大部分没设 TTL(如用户画像、配置项),必须用
allkeys-lru或allkeys-lfu;其中allkeys-lfu更适合长期运行、访问模式稳定的 pubsub 场景(例如固定 topic 的日志广播) - 绝对不要选
noeviction或volatile-random:前者让PUBLISH必然失败,后者淘汰不可控,可能删掉关键控制 key
注意:volatile-lfu 和 allkeys-lfu 要求 Redis ≥ 4.0,且比 LRU 多消耗约 2–3% 内存记录访问频次。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
如何安全地在线切换淘汰策略
生产环境不能直接改 redis.conf 后重启 —— PUBSUB 连接会断开,客户端重连期间消息丢失。必须用 CONFIG SET 动态生效:
- 先确认当前内存压力:
redis-cli INFO memory | grep -E "(used_memory|maxmemory|mem_fragmentation_ratio)" - 临时切策略(立即生效):
redis-cli CONFIG SET maxmemory-policy allkeys-lru - 验证是否生效:
redis-cli CONFIG GET maxmemory-policy应返回新值 - 观察 1–2 分钟:
redis-cli INFO stats | grep expired_keys若数值开始增长,说明淘汰已启动
⚠️ 风险点:切换瞬间若内存已满,Redis 会立刻执行一轮淘汰,可能造成短时延迟尖峰(evict.c 中的同步扫描)。建议在低峰期操作,或提前用 redis-cli --bigkeys 清理几个百 MB 级大 key 缓解压力。
比调策略更关键的是控制 PUBSUB 缓冲区
淘汰策略治标,但 PUBSUB 的输出缓冲区才是内存暴增主因。Redis 默认不限制单个 client 的 output buffer,一个卡住的订阅者就能撑爆内存:
- 检查异常 client:
redis-cli CLIENT LIST | grep -E "(omem|omem|cmd)",重点关注omem(output buffer 内存)> 10MB 的连接 - 限制缓冲区(推荐):在
redis.conf中加两行:client-output-buffer-limit pubsub 32mb 8mb 60
含义:pubsub 类型 client 缓冲区硬限 32MB,软限 8MB 持续 60 秒则断连 - 定期清理失效订阅:应用层实现心跳机制,超时未响应的 client 主动
UNSUBSCRIBE
真正压垮 Redis 的往往不是缓存数据,而是堆积的 pubsub 消息。淘汰策略只是兜底,缓冲区管控才是根因。

















