朕喜技术(ZhenxiJS)
2026.09.02
1 分钟阅读

Redis 内存写满拒绝写入:OOM command not allowed when used memory > 'maxmemory' 排查与解决

问题背景

生产环境的 Redis 实例突然报错:OOM command not allowed when used memory > 'maxmemory',所有写操作(SET、LPUSH、HSET 等)全部失败。这是因为 Redis 的 maxmemory 被设置为 10MB,而 maxmemory-policy 为 noeviction(不淘汰任何 key)。当 used_memory 达到 maxmemory 上限后,所有写操作都会被拒绝。

技术栈:Redis 7.x, Docker, redis-cli, Python redis 库 涉及配置:maxmemory、maxmemory-policy、noeviction、allkeys-lru、volatile-lru

复现步骤

环境准备

# 启动 Redis 容器(maxmemory=10mb, noeviction)
docker run -d --name redis-maxmemory-demo \
  -p 16379:6379 \
  -v $(pwd)/redis.conf:/usr/local/etc/redis/redis.conf \
  redis:7-alpine \
  redis-server /usr/local/etc/redis/redis.conf

填充内存至满

# 批量写入 1KB 数据直到内存打满
for i in $(seq 1 12000); do
  value=$(printf 'x%.0s' $(seq 1 1024))
  docker exec redis-maxmemory-demo redis-cli set "bulk_key_${i}" "$value"
done

触发 OOM 错误

$ docker exec redis-maxmemory-demo redis-cli SET new_key "new_value"
(error) OOM command not allowed when used memory > 'maxmemory'.

根本原因

Redis 的内存管理机制:

  1. maxmemory 限制了 Redis 最大可用内存(10MB)
  2. maxmemory-policy=noeviction 表示内存满了后不淘汰任何 key
  3. 当 used_memory 达到 maxmemory 后,所有写操作都会返回 OOM 错误
  4. 读操作不受影响,但应用无法写入缓存/会话数据,导致核心功能瘫痪

根因分析:

  • 配置失误:管理员没有正确评估数据量,设置的 maxmemory 过小
  • 策略不当:noeviction 策略虽然安全但过于保守,生产环境不适合作为默认策略
  • 缺少 TTL:缓存/会话数据未设置过期时间,导致 key 无限增长
  • 缺少监控:没有对内存使用率设置告警,等到问题爆发才被发现

解决办法

方案 1:调整淘汰策略为 allkeys-lru(应急)

# 临时修改(不持久化)
docker exec redis-maxmemory-demo redis-cli CONFIG SET maxmemory-policy allkeys-lru

# 持久化修改
# 编辑 redis.conf 加入:maxmemory-policy allkeys-lru
# 然后重启 Redis

优点:立即恢复写入能力,Redis 自动淘汰最久未使用的 key 缺点:可能淘汰重要数据,不适合存放关键数据

方案 2:设置 TTL + volatile-lru(推荐)

# 设置淘汰策略
docker exec redis-maxmemory-demo redis-cli CONFIG SET maxmemory-policy volatile-lru

# 业务代码中为所有缓存/会话 key 设置过期时间
redis-cli SET session:user001 "{\"user_id\": 1}" EX 3600
redis-cli SET cache:page_home "<html>...</html>" EX 1800

优点:只淘汰设置了 TTL 的 key,保护永久数据,适合缓存/会话场景 缺点:如果数据都没有设置 TTL,策略将不会淘汰任何 key(等同 noeviction)

方案 3:增大 maxmemory(硬件允许时)

docker exec redis-maxmemory-demo redis-cli CONFIG SET maxmemory 256mb

优点:简单直接 缺点:需要评估物理内存余量,只治标不治本

最佳实践组合

  1. 设置合理的 maxmemory(物理内存的 50%-70%)
  2. 使用 volatile-lru 作为淘汰策略
  3. 为所有缓存/会话 key 设置 TTL
  4. 定期用 redis-cli --scan --pattern '*' 检查无 TTL 的 key
  5. 通过 INFO memory 或监控系统持续关注内存使用率

验证

修复后验证写入正常:

# 验证 SET
$ docker exec redis-maxmemory-demo redis-cli SET new_key "new_value"
OK

# 验证 LPUSH
$ docker exec redis-maxmemory-demo redis-cli LPUSH new_list "item"
1

# 验证 HSET
$ docker exec redis-maxmemory-demo redis-cli HSET new_hash field "value"
1

# 验证淘汰统计
$ docker exec redis-maxmemory-demo redis-cli INFO stats | grep evicted_keys
evicted_keys:92178

经验与避坑

关键经验

  1. Redis 的 maxmemory-policy 默认值是 noeviction,生产环境务必修改
  2. 缓存和会话数据一定要设置 TTL,否则 Redis 会无限增长直到内存打满
  3. volatile-lru 是最适合缓存场景的策略:既能自动淘汰过期数据,又不会影响永久 key
  4. allkeys-lru 不适合存放关键数据,因为它会淘汰任何 key(包括永久 key)
  5. 监控 Redis 内存使用率,超过 80% 就应该告警

常见错误

  1. 只改 maxmemory 不改 maxmemory-policy:内存不够时仍然拒绝写入
  2. 设置 allkeys-lru 后不清除无 TTL 的 key:key 会被持续淘汰,可能影响业务
  3. 使用 FLUSHDB 清空数据:虽然可以立即恢复,但会丢失所有数据
  4. 只在运行时 CONFIG SET 修改配置:重启后配置丢失,必须同步修改配置文件

排查命令速查

# 查看内存配置
redis-cli CONFIG GET maxmemory
redis-cli CONFIG GET maxmemory-policy

# 查看内存使用
redis-cli INFO memory | grep -E "used_memory|maxmemory"

# 查看 key 数量
redis-cli DBSIZE

# 查看淘汰统计
redis-cli INFO stats | grep evicted_keys

# 查看无 TTL 的 key
redis-cli --scan --pattern '*' | while read key; do
  ttl=$(redis-cli TTL "$key")
  if [ "$ttl" = "-1" ]; then echo "无TTL: $key"; fi
done

# 查看最大内存占用 key(按内存排序)
redis-cli --bigkeys

快速应急清单

  1. CONFIG SET maxmemory-policy allkeys-lru(立即恢复写入)
  2. 排查并清理无 TTL 的 key
  3. CONFIG SET maxmemory-policy volatile-lru(切换回安全策略)
  4. 同步修改 redis.conf 配置文件
  5. 增加内存使用监控和告警

版权声明

本文为博主原创文章,遵循 CC BY-NC-SA 4.0 许可协议。 转载请注明出处:http://localhost/blogs/2026-09-02-0800-redis-maxmemory-oom

分享这篇文章