外观
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接近或达到maxmemorymem_fragmentation_ratio过高(如大于 1.5)evicted_keys数量突然增加expired_keys数量异常
内存不足处理方法
1. 紧急处理措施
1.1 增加 Redis 可用内存
bash
# 临时增加 maxmemory 配置
redis-cli CONFIG SET maxmemory 2gb
# 保存配置到文件
redis-cli CONFIG REWRITE1.2 执行内存回收
bash
# 尝试释放内存碎片
redis-cli MEMORY PURGE
# 触发 RDB 持久化,可能会释放一些内存
redis-cli BGSAVE1.3 删除大键或过期数据
bash
# 查找大键
redis-cli --bigkeys
# 删除特定大键
redis-cli DEL large_key
# 删除过期数据
redis-cli SCAN 0 MATCH * --count 1000 | xargs -r redis-cli DEL1.4 切换内存淘汰策略
bash
# 临时切换为 allkeys-lru 策略
redis-cli CONFIG SET maxmemory-policy allkeys-lru
# 保存配置到文件
redis-cli CONFIG REWRITE2. 根本原因分析
2.1 分析内存使用情况
bash
# 查看内存使用详情
redis-cli INFO memory
# 查看内存碎片情况
redis-cli MEMORY STATS
# 查看大键
redis-cli --bigkeys
# 查看键空间统计
redis-cli INFO keyspace2.2 分析命令执行情况
bash
# 查看慢查询日志
redis-cli SLOWLOG GET 100
# 查看命令统计
redis-cli INFO commandstats2.3 分析客户端连接
bash
# 查看客户端连接情况
redis-cli CLIENT LIST
# 查看连接数统计
redis-cli INFO clients3. 长期解决方案
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 753.2 优化数据结构
- 使用合适的数据结构存储数据
- 拆分大键为多个小键
- 使用 Redis 5.0+ 的
UNLINK命令替代DEL命令删除大键 - 合理设置键的过期时间
3.3 实现分片或集群
- 垂直分片:将不同类型的数据存储到不同的 Redis 实例
- 水平分片:使用 Redis Cluster 或客户端分片将数据分布到多个 Redis 实例
3.4 优化应用程序
- 减少不必要的数据写入
- 优化缓存策略,避免缓存击穿和缓存雪崩
- 实现合理的缓存过期机制
- 使用连接池管理 Redis 连接
内存不足预防措施
1. 合理配置内存参数
- 根据业务需求设置合适的
maxmemory值 - 选择合适的内存淘汰策略
- 配置内存碎片整理
- 监控内存使用率,设置告警阈值
2. 优化数据管理
- 定期清理过期数据
- 监控和处理大键
- 合理设置键的过期时间
- 使用压缩数据格式存储数据
3. 实施监控和告警
- 监控 Redis 内存使用率,设置告警阈值(如 80%)
- 监控内存碎片率,设置告警阈值(如 1.5)
- 监控
evicted_keys和expired_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: 出现该错误时,可以采取以下措施:
紧急处理:
- 临时增加
maxmemory配置 - 切换到更激进的内存淘汰策略
- 删除大键或过期数据
- 临时增加
根本原因分析:
- 分析内存使用情况,找出占用内存最多的键
- 检查是否存在内存泄漏
- 分析业务数据增长情况
长期解决方案:
- 优化内存配置
- 实施分片或集群
- 优化应用程序设计
Q2: 如何监控 Redis 内存使用情况?
A2: 可以通过以下方式监控 Redis 内存使用情况:
使用 Redis 命令:
bashredis-cli INFO memory redis-cli MEMORY STATS使用监控工具:
- Prometheus + Grafana:监控 Redis 内存使用率、内存碎片率等指标
- Redis Exporter:提供 Redis 详细的监控指标
- 第三方监控服务:如 Datadog、New Relic 等
设置告警:
- 内存使用率超过 80% 时告警
- 内存碎片率超过 1.5 时告警
evicted_keys数量突然增加时告警
Q3: 如何处理 Redis 内存碎片?
A3: 可以通过以下方式处理 Redis 内存碎片:
启用内存碎片整理:
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执行手动内存碎片整理:
bashredis-cli MEMORY PURGE重启 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 内存使用:
选择合适的数据结构:
- 小整数使用 int 编码
- 短字符串使用 embstr 编码
- 合理使用 Hash、List、Set、Sorted Set 等数据结构
压缩数据:
- 使用 Redis 5.0+ 的 LIST_PACK 编码
- 使用压缩算法(如 Snappy、LZ4)压缩数据
- 优化序列化格式(如使用 MessagePack 替代 JSON)
拆分大键:
- 将大 Hash 拆分为多个小 Hash
- 将大 List 拆分为多个小 List
- 使用 Redis Cluster 进行水平分片
合理设置过期时间:
- 为临时数据设置合适的过期时间
- 定期清理过期数据
- 使用惰性删除和定期删除机制
Q6: Redis 内存淘汰策略如何选择?
A6: 选择 Redis 内存淘汰策略时,需要考虑以下因素:
业务场景:
- 缓存场景:优先考虑 LRU 或 LFU 策略
- 数据不能丢失的场景:使用 noeviction 策略
- 数据访问频率均匀:使用 random 策略
数据特点:
- 键有过期时间:考虑使用 volatile-* 策略
- 键无过期时间:考虑使用 allkeys-* 策略
- 关注键的剩余时间:使用 volatile-ttl 策略
Redis 版本:
- Redis 4.0+ 支持 LFU 策略,更适合访问频率不均匀的场景
- 旧版本 Redis 只能使用 LRU、TTL 或 random 策略
最佳实践
1. 内存配置最佳实践
- 合理设置 maxmemory:根据业务需求和服务器内存大小设置合适的
maxmemory值,建议预留 20-30% 的内存给系统其他进程 - 选择合适的内存淘汰策略:根据业务场景选择合适的内存淘汰策略
- 启用内存碎片整理:对于频繁写入和删除的场景,建议启用内存碎片整理
- 监控内存使用率:设置内存使用率告警阈值,及时发现内存不足问题
2. 数据管理最佳实践
- 合理设置过期时间:为临时数据设置合适的过期时间,避免过期数据占用内存
- 监控和处理大键:定期查找和处理大键,避免大键占用过多内存
- 优化数据结构:选择合适的数据结构,提高内存利用率
- 压缩数据:对于大型数据,考虑使用压缩算法减少内存占用
3. 监控和告警最佳实践
- 监控关键指标:内存使用率、内存碎片率、
evicted_keys、expired_keys等 - 设置合理的告警阈值:内存使用率 80%、内存碎片率 1.5、
evicted_keys突增等 - 定期分析监控数据:定期分析 Redis 内存使用趋势,提前发现潜在问题
- 建立完善的告警机制:确保告警能够及时通知到相关人员
4. 高可用和容灾最佳实践
- 实施 Redis Cluster:使用 Redis Cluster 实现水平扩展,提高系统容量
- 配置主从复制:确保数据有备份,避免单点故障
- 实施持久化:配置合理的持久化策略,确保数据安全
- 定期进行灾备演练:确保在发生内存不足等问题时能够快速恢复
5. 应用程序最佳实践
- 实现合理的缓存策略:避免缓存击穿和缓存雪崩
- 使用连接池管理 Redis 连接:避免连接泄漏
- 优化数据序列化:使用高效的序列化格式,减少内存占用
- 实现限流和熔断:避免突发流量导致 Redis 内存不足
