Skip to content

Redis 内存不足处理

Redis 是一种内存数据库,内存不足(Out of Memory,OOM)是 Redis 运维中常见的问题之一。本文档将详细介绍 Redis 内存不足的原因、症状、处理方法和预防措施。

内存不足原因

1. 数据量增长过快

  • 业务数据量快速增长,超过了 Redis 配置的最大内存限制
  • 缓存击穿或缓存雪崩导致大量数据写入 Redis
  • 未设置合理的过期时间,导致过期数据无法及时清理

2. 内存配置不合理

  • maxmemory 配置值过小,无法满足业务需求
  • 内存淘汰策略配置不当,导致无法有效释放内存
  • 未考虑系统其他进程的内存占用

3. 内存碎片

  • Redis 内存分配器(如 jemalloc)产生内存碎片
  • 频繁的写入和删除操作导致内存碎片增加
  • 内存碎片率过高,导致实际可用内存减少

4. 大键问题

  • 单个键值过大,占用大量内存
  • 大量小键占用过多内存
  • 集合类型(如 Hash、List、Set、Sorted Set)中包含过多元素

5. 内存泄漏

  • 应用程序未正确释放 Redis 连接
  • Redis 客户端库存在内存泄漏
  • Redis 本身的 bug 导致内存泄漏

内存不足症状

1. 错误日志

Redis 日志中出现 "OOM command not allowed when used memory > 'maxmemory'" 错误

2. 命令执行失败

  • 写入命令(如 SET、LPUSH、HSET 等)执行失败
  • 返回 "OOM command not allowed" 错误

3. 性能下降

  • Redis 响应时间变长
  • CPU 使用率升高
  • 内存使用率接近或达到 100%

4. 系统层面症状

  • 系统内存使用率接近 100%
  • 系统出现 Swap 使用
  • 进程被 OOM Killer 杀死

5. 监控指标异常

  • used_memory 接近或达到 maxmemory
  • mem_fragmentation_ratio 过高(如大于 1.5)
  • evicted_keys 数量突然增加
  • expired_keys 数量异常

内存不足处理方法

1. 紧急处理措施

1.1 增加 Redis 可用内存

bash
# 临时增加 maxmemory 配置
redis-cli CONFIG SET maxmemory 2gb

# 保存配置到文件
redis-cli CONFIG REWRITE

1.2 执行内存回收

bash
# 尝试释放内存碎片
redis-cli MEMORY PURGE

# 触发 RDB 持久化,可能会释放一些内存
redis-cli BGSAVE

1.3 删除大键或过期数据

bash
# 查找大键
redis-cli --bigkeys

# 删除特定大键
redis-cli DEL large_key

# 删除过期数据
redis-cli SCAN 0 MATCH * --count 1000 | xargs -r redis-cli DEL

1.4 切换内存淘汰策略

bash
# 临时切换为 allkeys-lru 策略
redis-cli CONFIG SET maxmemory-policy allkeys-lru

# 保存配置到文件
redis-cli CONFIG REWRITE

2. 根本原因分析

2.1 分析内存使用情况

bash
# 查看内存使用详情
redis-cli INFO memory

# 查看内存碎片情况
redis-cli MEMORY STATS

# 查看大键
redis-cli --bigkeys

# 查看键空间统计
redis-cli INFO keyspace

2.2 分析命令执行情况

bash
# 查看慢查询日志
redis-cli SLOWLOG GET 100

# 查看命令统计
redis-cli INFO commandstats

2.3 分析客户端连接

bash
# 查看客户端连接情况
redis-cli CLIENT LIST

# 查看连接数统计
redis-cli INFO clients

3. 长期解决方案

3.1 优化内存配置

txt
# redis.conf
# 设置合理的 maxmemory
maxmemory 4gb

# 设置合适的内存淘汰策略
maxmemory-policy allkeys-lru

# 配置内存碎片整理
activedefrag yes
active-defrag-threshold-lower 10
active-defrag-threshold-upper 100
active-defrag-cycle-min 5
active-defrag-cycle-max 75

3.2 优化数据结构

  • 使用合适的数据结构存储数据
  • 拆分大键为多个小键
  • 使用 Redis 5.0+ 的 UNLINK 命令替代 DEL 命令删除大键
  • 合理设置键的过期时间

3.3 实现分片或集群

  • 垂直分片:将不同类型的数据存储到不同的 Redis 实例
  • 水平分片:使用 Redis Cluster 或客户端分片将数据分布到多个 Redis 实例

3.4 优化应用程序

  • 减少不必要的数据写入
  • 优化缓存策略,避免缓存击穿和缓存雪崩
  • 实现合理的缓存过期机制
  • 使用连接池管理 Redis 连接

内存不足预防措施

1. 合理配置内存参数

  • 根据业务需求设置合适的 maxmemory
  • 选择合适的内存淘汰策略
  • 配置内存碎片整理
  • 监控内存使用率,设置告警阈值

2. 优化数据管理

  • 定期清理过期数据
  • 监控和处理大键
  • 合理设置键的过期时间
  • 使用压缩数据格式存储数据

3. 实施监控和告警

  • 监控 Redis 内存使用率,设置告警阈值(如 80%)
  • 监控内存碎片率,设置告警阈值(如 1.5)
  • 监控 evicted_keysexpired_keys 数量
  • 监控大键和热点键

4. 定期进行容量规划

  • 根据业务增长趋势进行容量规划
  • 预留足够的内存余量
  • 考虑使用 Redis Cluster 进行水平扩展
  • 定期测试 Redis 在高负载下的表现

5. 优化应用程序设计

  • 实现合理的缓存策略
  • 避免缓存击穿和缓存雪崩
  • 优化数据序列化和反序列化
  • 使用连接池管理 Redis 连接

内存淘汰策略

Redis 提供了多种内存淘汰策略,用于在内存不足时自动释放内存:

1. 基于过期时间的淘汰策略

  • volatile-lru:从已设置过期时间的键中,淘汰最近最少使用的键
  • volatile-ttl:从已设置过期时间的键中,淘汰剩余时间最短的键
  • volatile-random:从已设置过期时间的键中,随机淘汰键
  • volatile-lfu:从已设置过期时间的键中,淘汰最少使用频率的键(Redis 4.0+)

2. 基于所有键的淘汰策略

  • allkeys-lru:从所有键中,淘汰最近最少使用的键
  • allkeys-random:从所有键中,随机淘汰键
  • allkeys-lfu:从所有键中,淘汰最少使用频率的键(Redis 4.0+)

3. 不淘汰策略

  • noeviction:不淘汰任何键,内存不足时拒绝写入命令

4. 淘汰策略选择建议

场景推荐策略
缓存场景,键有过期时间volatile-lru 或 volatile-lfu
缓存场景,键无过期时间allkeys-lru 或 allkeys-lfu
数据不能丢失的场景noeviction(需配合其他措施)
数据访问频率均匀allkeys-random 或 volatile-random
关注键的剩余时间volatile-ttl

常见问题(FAQ)

Q1: Redis 出现 "OOM command not allowed" 错误怎么办?

A1: 出现该错误时,可以采取以下措施:

  1. 紧急处理

    • 临时增加 maxmemory 配置
    • 切换到更激进的内存淘汰策略
    • 删除大键或过期数据
  2. 根本原因分析

    • 分析内存使用情况,找出占用内存最多的键
    • 检查是否存在内存泄漏
    • 分析业务数据增长情况
  3. 长期解决方案

    • 优化内存配置
    • 实施分片或集群
    • 优化应用程序设计

Q2: 如何监控 Redis 内存使用情况?

A2: 可以通过以下方式监控 Redis 内存使用情况:

  1. 使用 Redis 命令

    bash
    redis-cli INFO memory
    redis-cli MEMORY STATS
  2. 使用监控工具

    • Prometheus + Grafana:监控 Redis 内存使用率、内存碎片率等指标
    • Redis Exporter:提供 Redis 详细的监控指标
    • 第三方监控服务:如 Datadog、New Relic 等
  3. 设置告警

    • 内存使用率超过 80% 时告警
    • 内存碎片率超过 1.5 时告警
    • evicted_keys 数量突然增加时告警

Q3: 如何处理 Redis 内存碎片?

A3: 可以通过以下方式处理 Redis 内存碎片:

  1. 启用内存碎片整理

    txt
    # redis.conf
    activedefrag yes
    active-defrag-threshold-lower 10
    active-defrag-threshold-upper 100
    active-defrag-cycle-min 5
    active-defrag-cycle-max 75
  2. 执行手动内存碎片整理

    bash
    redis-cli MEMORY PURGE
  3. 重启 Redis 实例

    • 对于无法通过其他方式解决的严重内存碎片问题,可以考虑重启 Redis 实例
    • 重启前需要确保数据已持久化

Q4: 如何查找 Redis 中的大键?

A4: 可以使用以下命令查找 Redis 中的大键:

bash
# 使用 --bigkeys 选项
redis-cli --bigkeys

# 使用 MEMORY USAGE 命令查看单个键的内存使用
redis-cli MEMORY USAGE key

# 使用 SCAN 命令结合 MEMORY USAGE 查找大键
redis-cli SCAN 0 MATCH * --count 1000 | xargs -I {} redis-cli MEMORY USAGE {}

Q5: 如何优化 Redis 内存使用?

A5: 可以通过以下方式优化 Redis 内存使用:

  1. 选择合适的数据结构

    • 小整数使用 int 编码
    • 短字符串使用 embstr 编码
    • 合理使用 Hash、List、Set、Sorted Set 等数据结构
  2. 压缩数据

    • 使用 Redis 5.0+ 的 LIST_PACK 编码
    • 使用压缩算法(如 Snappy、LZ4)压缩数据
    • 优化序列化格式(如使用 MessagePack 替代 JSON)
  3. 拆分大键

    • 将大 Hash 拆分为多个小 Hash
    • 将大 List 拆分为多个小 List
    • 使用 Redis Cluster 进行水平分片
  4. 合理设置过期时间

    • 为临时数据设置合适的过期时间
    • 定期清理过期数据
    • 使用惰性删除和定期删除机制

Q6: Redis 内存淘汰策略如何选择?

A6: 选择 Redis 内存淘汰策略时,需要考虑以下因素:

  1. 业务场景

    • 缓存场景:优先考虑 LRU 或 LFU 策略
    • 数据不能丢失的场景:使用 noeviction 策略
    • 数据访问频率均匀:使用 random 策略
  2. 数据特点

    • 键有过期时间:考虑使用 volatile-* 策略
    • 键无过期时间:考虑使用 allkeys-* 策略
    • 关注键的剩余时间:使用 volatile-ttl 策略
  3. Redis 版本

    • Redis 4.0+ 支持 LFU 策略,更适合访问频率不均匀的场景
    • 旧版本 Redis 只能使用 LRU、TTL 或 random 策略

最佳实践

1. 内存配置最佳实践

  • 合理设置 maxmemory:根据业务需求和服务器内存大小设置合适的 maxmemory 值,建议预留 20-30% 的内存给系统其他进程
  • 选择合适的内存淘汰策略:根据业务场景选择合适的内存淘汰策略
  • 启用内存碎片整理:对于频繁写入和删除的场景,建议启用内存碎片整理
  • 监控内存使用率:设置内存使用率告警阈值,及时发现内存不足问题

2. 数据管理最佳实践

  • 合理设置过期时间:为临时数据设置合适的过期时间,避免过期数据占用内存
  • 监控和处理大键:定期查找和处理大键,避免大键占用过多内存
  • 优化数据结构:选择合适的数据结构,提高内存利用率
  • 压缩数据:对于大型数据,考虑使用压缩算法减少内存占用

3. 监控和告警最佳实践

  • 监控关键指标:内存使用率、内存碎片率、evicted_keysexpired_keys
  • 设置合理的告警阈值:内存使用率 80%、内存碎片率 1.5、evicted_keys 突增等
  • 定期分析监控数据:定期分析 Redis 内存使用趋势,提前发现潜在问题
  • 建立完善的告警机制:确保告警能够及时通知到相关人员

4. 高可用和容灾最佳实践

  • 实施 Redis Cluster:使用 Redis Cluster 实现水平扩展,提高系统容量
  • 配置主从复制:确保数据有备份,避免单点故障
  • 实施持久化:配置合理的持久化策略,确保数据安全
  • 定期进行灾备演练:确保在发生内存不足等问题时能够快速恢复

5. 应用程序最佳实践

  • 实现合理的缓存策略:避免缓存击穿和缓存雪崩
  • 使用连接池管理 Redis 连接:避免连接泄漏
  • 优化数据序列化:使用高效的序列化格式,减少内存占用
  • 实现限流和熔断:避免突发流量导致 Redis 内存不足