如何触发 Redis 的 OOM(Out of Memory)错误

举报
林欣 发表于 2025/06/29 23:20:47 2025/06/29
【摘要】 Redis 的 OOM(Out of Memory)错误通常发生在内存使用超过配置限制(maxmemory)且没有有效的淘汰策略(Eviction Policy)释放内存时。以下是触发 OOM 错误的场景、原理及防范方法,帮助你理解并避免此类问题。 一、OOM 错误的触发条件Redis 触发 OOM 需满足以下两个核心条件:内存使用超过 maxmemory 限制在 redis.conf 中通...

Redis 的 OOM(Out of Memory)错误通常发生在内存使用超过配置限制(maxmemory)且没有有效的淘汰策略(Eviction Policy)释放内存时。以下是触发 OOM 错误的场景、原理及防范方法,帮助你理解并避免此类问题。


一、OOM 错误的触发条件

Redis 触发 OOM 需满足以下两个核心条件:

  1. 内存使用超过 maxmemory 限制

    • redis.conf 中通过 maxmemory 参数设置内存上限(如 maxmemory 1gb)。
    • used_memory(Redis 实际使用内存) ≥ maxmemory 时,进入内存限制状态。
  2. 未配置或禁用淘汰策略

    • 如果 maxmemory-policy 设置为 noeviction(默认值),Redis 会拒绝所有写入操作并返回 OOM 错误。
    • 其他策略(如 volatile-lruallkeys-lru)会主动淘汰键以释放内存,避免 OOM。

二、触发 OOM 的常见场景

1. 配置 noeviction 策略且内存耗尽

操作步骤

  1. 修改 redis.conf
    maxmemory 100mb          # 设置较小的内存限制
    maxmemory-policy noeviction  # 禁用淘汰策略
    
  2. 重启 Redis 或动态加载配置:
    redis-cli CONFIG SET maxmemory 100mb
    redis-cli CONFIG SET maxmemory-policy noeviction
    
  3. 持续写入数据直到内存耗尽:
    for i in {1..100000}; do
        redis-cli SET "key:$i" "$(head -c 1024 /dev/urandom)"  # 写入 1KB 数据
    done
    

结果
当内存超过 100MB 时,Redis 返回错误:

(error) OOM command not allowed when used memory > 'maxmemory'.

2. 大量数据写入且淘汰策略失效

场景

  • 配置了淘汰策略(如 volatile-lru),但所有键均未设置过期时间(EXPIRE),导致无法淘汰。

操作步骤

  1. 设置策略为 volatile-lru(需键有过期时间才能生效):
    redis-cli CONFIG SET maxmemory-policy volatile-lru
    
  2. 写入大量无过期时间的键:
    for i in {1..500000}; do
        redis-cli SET "key:$i" "value"  # 无过期时间
    done
    
  3. 继续写入新键:
    redis-cli SET "new_key" "data"  # 触发 OOM(无键可淘汰)
    

结果
Redis 返回 OOM 错误,因为所有键均未过期,volatile-lru 无法淘汰。

3. 内存碎片化导致实际可用内存不足

场景

  • 内存碎片率(mem_fragmentation_ratio)过高(如 >2.0),即使 used_memory 未达 maxmemory,系统也可能因物理内存不足触发 OOM。

操作步骤

  1. 模拟内存碎片化:
    • 频繁写入/删除大键(如 1MB 的键):
      for i in {1..1000}; do
          redis-cli SET "large_key:$i" "$(head -c 1048576 /dev/urandom)"
          redis-cli DEL "large_key:$i"
      done
      
  2. 持续写入数据直到系统 OOM(需结合系统内存限制)。

结果
Redis 或系统内核可能终止 Redis 进程(取决于系统 OOM Killer 配置)。


三、OOM 错误的底层原理

1. Redis 的内存管理流程

  1. 客户端发送写入命令(如 SET)。
  2. Redis 检查当前内存使用:
    • used_memory < maxmemory,执行写入。
    • used_memory ≥ maxmemory,根据 maxmemory-policy 决定行为:
      • noeviction:淘汰符合策略的键,腾出空间后写入。
      • noeviction:拒绝写入并返回 OOM 错误。

2. 系统级 OOM Killer

  • 当 Redis 申请的内存超过系统可用物理内存 + Swap 时,Linux 内核的 OOM Killer 可能终止 Redis 进程。
  • 触发条件
    • 系统 oom_score_adj 较高(Redis 默认值通常为 0)。
    • 无足够 Swap 空间。

检查方法

# 查看 Redis 进程的 OOM 分数
cat /proc/$(pidof redis-server)/oom_score_adj

# 查看系统内存状态
free -h

四、如何模拟与测试 OOM

1. 使用 redis-benchmark 快速触发

# 限制内存为 100MB,策略为 noeviction
redis-cli CONFIG SET maxmemory 100mb
redis-cli CONFIG SET maxmemory-policy noeviction

# 用 benchmark 持续写入(键大小 1KB)
redis-benchmark -t set -n 1000000 -r 1000000 -d 1024

预期结果
部分写入命令会返回 OOM 错误。

2. 结合 memtier_benchmark(更灵活)

# 安装 memtier_benchmark(Ubuntu)
sudo apt-get install memtier_benchmark

# 运行测试(100 线程,100万请求,键大小 1KB)
memtier_benchmark --server=127.0.0.1 --port=6379 --protocol=redis \
  --clients=100 --threads=10 --test-time=30 --key-pattern=S:S \
  --key-minimum=1 --key-maximum=1000000 --data-size=1024 \
  --pipeline=1 --command="SET __key__ __data__"

五、防范与处理 OOM 的方法

1. 合理配置 maxmemory 和淘汰策略

# 示例:设置内存上限为 4GB,使用 LRU 淘汰策略
maxmemory 4gb
maxmemory-policy allkeys-lru

策略选择

  • volatile-lru:优先淘汰有过期时间的键(适合缓存场景)。
  • allkeys-lru:淘汰所有键中的最近最少使用键(适合无过期时间的键)。
  • volatile-ttl:淘汰剩余 TTL 最短的键(适合短过期键)。

2. 监控内存使用

# 实时监控内存和碎片率
watch -n 1 "redis-cli info memory | grep -E 'used_memory|mem_fragmentation_ratio|maxmemory'"

关键指标

  • used_memory_rss:系统实际分配内存(含碎片)。
  • mem_fragmentation_ratio:碎片率(>1.5 需关注)。

3. 优化数据结构与键设计

  • 避免大键(如单个哈希包含数万字段)。
  • 对大键使用分片(如 user:1000:profileuser:1000:stats)。
  • 启用压缩列表(调整 hash-max-ziplist-entries 等参数)。

4. 启用内存碎片整理

# Redis 4.0+ 配置
activedefrag yes
active-defrag-threshold-lower 10  # 碎片率 >10% 时启动整理

5. 系统级防护

  • 限制 Redis 进程的 OOM 优先级
    # 降低 Redis 的 OOM 分数(避免被内核终止)
    echo -1000 > /proc/$(pidof redis-server)/oom_score_adj
    
  • 增加 Swap 空间(临时缓解方案):
    sudo fallocate -l 2G /swapfile
    sudo chmod 600 /swapfile
    sudo mkswap /swapfile
    sudo swapon /swapfile
    

六、总结

触发方式 条件 后果
noeviction + 内存耗尽 maxmemory-policy=noeviction,写入数据超过 maxmemory 返回 OOM command not allowed
淘汰策略失效 策略需依赖过期键(如 volatile-lru),但所有键均无过期时间 返回 OOM 错误
系统级 OOM Killer Redis 申请内存超过系统物理内存 + Swap Redis 进程被终止

最佳实践

  1. 生产环境禁用 noeviction:根据业务选择 volatile-lruallkeys-lru
  2. 设置合理的 maxmemory:通常为物理内存的 70%~80%。
  3. 监控碎片率:>1.5 时启用整理或优化数据结构。
  4. 测试环境模拟 OOM:验证系统的容错能力(如集群是否自动故障转移)。

通过以上方法,可有效避免 Redis OOM 错误,保障服务稳定性。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。