2021-04-02 19:19:05
当 Redis 内存告急时,可采取优化数据结构、限制过期时间、扩展存储策略等措施缓解问题,具体方案如下:
一、理解 Redis 内存耗尽的两种典型表现拒绝服务模式(默认配置)
当内存达到上限时,Redis 会拒绝所有写入或修改操作(如 SET、HSET、LPUSH 等),但读取(GET)和删除(DEL)操作仍可执行。
此模式可避免数据损坏,但会导致业务写入功能瘫痪,需立即处理。
虚拟内存模式(非推荐方案)
通过配置将部分冷数据交换到硬盘(类似 RDB 持久化),需手动启用且性能显著下降。
缺点:硬盘 I/O 延迟远高于内存,可能导致查询耗时增加 10 倍以上;Redis 设计初衷并非用硬盘扩展内存,此模式仅作为临时过渡方案。
使用更紧凑的数据类型
用 INTSET 替代 HASH 存储整数键值对(如用户年龄)。
用 ZIPLIST 编码的 LIST/HASH/ZSET 替代链表结构(需控制元素数量,通常小于 512 个)。
示例:HASH-MAX-ZIPLIST-ENTRIES 512 和 HASH-MAX-ZIPLIST-VALUE 64 可配置阈值。
压缩字符串存储
对大文本数据(如 JSON)启用压缩算法(如 Snappy 或 LZ4),但需权衡 CPU 开销。
示例:通过 Lua 脚本在写入前压缩,读取时解压。
启用主动淘汰机制
修改 maxmemory-policy 参数(默认 noeviction 拒绝写入),推荐以下策略:
volatile-lru:淘汰最近最少使用的带过期时间的数据。
allkeys-lru:淘汰全局最近最少使用的数据(适合无过期键的场景)。
volatile-ttl:淘汰即将过期的数据。
示例配置:CONFIG SET maxmemory-policy volatile-lru。
设置合理的过期时间
为临时数据(如会话、缓存)添加 EXPIRE 命令,避免长期占用内存。
示例:SET user:1001 "data" EX 3600(1 小时后过期)。
分片(Sharding)
客户端分片:通过哈希算法(如 CRC32)将键分散到多个 Redis 实例,需自行处理数据迁移和故障转移。
代理分片:使用 Twemproxy 或 Codis 等中间件,对客户端透明但增加延迟。
集群模式(Redis Cluster)
Redis 官方支持的分布式方案,自动处理数据分片和故障转移。
要求:至少 6 个节点(3 主 3 从),支持在线扩容。
示例命令:CLUSTER MEET <ip> <port> 添加节点。
增加单机内存
升级服务器物理内存(如从 32GB 扩展到 128GB)。
注意:Redis 单线程模型下,内存过大可能导致持久化(RDB/AOF)阻塞时间变长。
使用更高性能的存储介质
将 AOF 持久化文件放在 SSD 上,减少 I/O 瓶颈。
实时内存监控
使用 INFO memory 命令查看内存使用详情,关注以下指标:
used_memory:已用内存。
maxmemory:配置的内存上限。
mem_fragmentation_ratio:内存碎片率(>1.5 需优化)。
示例:watch -n 1 "redis-cli INFO memory | grep used_memory"。
设置告警阈值
通过 Prometheus + Grafana 监控 used_memory_rss(系统实际占用内存),当达到 maxmemory 的 80% 时触发告警。
定期清理无效数据
编写脚本扫描并删除过期或冗余数据,如:redis-cli --scan --pattern "temp:*" | xargs -L 1000 redis-cli DEL
避免大键(Big Key)
单个键值对超过 10KB 会显著影响性能,需拆分为多个小键。
检测命令:redis-cli --bigkeys。
谨慎使用持久化
RDB 快照会阻塞主线程,大内存下可能导致服务短暂不可用。
建议:低峰期执行 SAVE,或使用 BGSAVE 异步生成快照。
禁用虚拟内存
在 redis.conf 中确保 vm-enabled no,避免性能恶化。
通过以上措施,可系统性解决 Redis 内存不足问题,并根据业务场景选择最优方案(如优先优化数据结构,长期考虑集群扩展)。