朕喜技术(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 的内存管理机制:
maxmemory限制了 Redis 最大可用内存(10MB)maxmemory-policy=noeviction表示内存满了后不淘汰任何 key- 当
used_memory达到maxmemory后,所有写操作都会返回OOM错误 - 读操作不受影响,但应用无法写入缓存/会话数据,导致核心功能瘫痪
根因分析:
- 配置失误:管理员没有正确评估数据量,设置的
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
优点:简单直接 缺点:需要评估物理内存余量,只治标不治本
最佳实践组合
- 设置合理的
maxmemory(物理内存的 50%-70%) - 使用
volatile-lru作为淘汰策略 - 为所有缓存/会话 key 设置 TTL
- 定期用
redis-cli --scan --pattern '*'检查无 TTL 的 key - 通过
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
经验与避坑
关键经验
- Redis 的
maxmemory-policy默认值是noeviction,生产环境务必修改 - 缓存和会话数据一定要设置 TTL,否则 Redis 会无限增长直到内存打满
volatile-lru是最适合缓存场景的策略:既能自动淘汰过期数据,又不会影响永久 keyallkeys-lru不适合存放关键数据,因为它会淘汰任何 key(包括永久 key)- 监控 Redis 内存使用率,超过 80% 就应该告警
常见错误
- 只改
maxmemory不改maxmemory-policy:内存不够时仍然拒绝写入 - 设置
allkeys-lru后不清除无 TTL 的 key:key 会被持续淘汰,可能影响业务 - 使用
FLUSHDB清空数据:虽然可以立即恢复,但会丢失所有数据 - 只在运行时
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
快速应急清单
CONFIG SET maxmemory-policy allkeys-lru(立即恢复写入)- 排查并清理无 TTL 的 key
CONFIG SET maxmemory-policy volatile-lru(切换回安全策略)- 同步修改 redis.conf 配置文件
- 增加内存使用监控和告警
