
Redis 内存占用持续升高,常见原因有三类:数据量确实在增长;键没有设置合理过期时间;淘汰策略、碎片率或持久化机制让内存没有按预期释放。本文按线上排查顺序拆解 used_memory、used_memory_rss、碎片率、key 数量、过期策略和持久化影响,帮助你判断该清理数据、调参数还是扩容。
内存升高不一定等于泄漏。Redis 的很多设计会把删除和回收延后执行,部分内存还会交给分配器持有。先用指标判断内存花在哪里,再决定是否调整,避免把业务热数据误删。
一、先分清三个内存指标
连接 Redis 后先查看整体状态:
redis-cli INFO memory
重点看这些字段:
used_memory:Redis 分配器分配给数据结构和内部使用的内存。used_memory_rss:操作系统进程视角占用的物理内存。used_memory_dataset:数据集本身占用,扣除复制积压缓冲、客户端缓冲等开销。mem_fragmentation_ratio:used_memory_rss / used_memory的比值。maxmemory:配置的最大可用内存,0表示未限制。
如果 used_memory 和键数量同步增长,优先查业务写入和过期策略。如果 used_memory 稳定但 used_memory_rss 很高,可能是碎片或分配器没有归还内存。如果 maxmemory 为 0,Redis 不会主动限制数据规模,直到操作系统内存不足。
碎片率的常见判断方式:
- 1.0 到 1.5:多数场景可以接受。
- 明显高于 1.5:碎片偏多,要结合删除模式、页大小和 activedefrag 判断。
- 低于 1.0:常见于 Redis 使用 swap 或操作系统内存统计口径变化,需要先查系统内存压力。
二、确认是键增长还是单键变大
键数量是最直接的入口:
redis-cli DBSIZE
redis-cli INFO keyspace
输出里的 keys 表示键数量,expires 表示带过期时间的键数量,avg_ttl 是平均 TTL 的采样值。如果键数量持续上涨而 expires 很少,说明大量数据没有生命周期,缓存可能被当成永久存储使用。
接着按类型和大小采样:
redis-cli –bigkeys
redis-cli –memkeys
--bigkeys 只能采样,适合快速发现大集合;--memkeys 会按内存排序输出键,但需要内存分析支持。采样会带来额外开销,生产环境应放在低峰期执行,避免长时间扫描。
定位到可疑键后,再看具体类型、长度和编码:
redis-cli MEMORY USAGE
redis-cli OBJECT ENCODING
redis-cli DEBUG OBJECT
DEBUG OBJECT 会返回低层信息,部分托管 Redis 会禁用 DEBUG 命令。普通运维排查优先使用 MEMORY USAGE 和业务侧键命名规范定位归属。
三、过期和淘汰策略要一起看
先看当前配置:
redis-cli CONFIG GET maxmemory
redis-cli CONFIG GET maxmemory-policy
常用淘汰策略的行为不同:
noeviction:写满后新写入报错,读操作可以继续。适合把 Redis 当存储使用的场景。allkeys-lru:从所有键里淘汰最近最少使用的键,常见缓存场景。volatile-lru:只淘汰设置了过期时间的键。如果没有键带 TTL,行为会接近写失败。allkeys-lfu:按访问频率淘汰,适合热点明显的缓存。volatile-ttl:优先淘汰剩余存活时间较短的键。allkeys-random和volatile-random:随机淘汰,通常较少用于核心业务。
策略选择要和数据定位一致。缓存可以按命中率选择 LRU 或 LFU;如果 Redis 保存订单、队列、Session 等仍有业务语义的数据,直接配置 allkeys 类策略可能删掉关键数据。生产修改前先记录原值,用 CONFIG SET 小范围验证,再写入配置文件和部署模板。
四、删除之后内存没有下降的原因
Redis 删除键后,内存不一定会立刻还给操作系统。进程分配器可能继续持有已释放页,used_memory_rss 因此保持较高水平。这种情况要看 used_memory 是否已经下降,如果业务侧内存指标正常,只是 RSS 偏高,不宜盲目重启。
异步删除也会影响观察结果。UNLINK、过期删除和 FLUSHALL ASYNC 可能由后台线程逐步完成,短时间内内存不会瞬间回落。如果使用了主从复制,还要考虑副本重新同步、复制积压缓冲区和输出缓冲区对内存的临时影响。
大量短键反复创建删除,容易产生碎片。Redis 4.0 以上支持主动碎片整理:
redis-cli CONFIG GET activedefrag
redis-cli CONFIG GET active-defrag-*
主动碎片整理会消耗 CPU,适合低峰期逐步启用,并通过监控观察延迟变化。托管 Redis 是否开放该能力,以服务商标配为准。
五、持久化与复制带来的内存开销
RDB 和 AOF 都会影响内存观察:
redis-cli INFO persistence
RDB 保存需要 fork 子进程。数据量大时,fork 可能带来短暂内存峰值;如果操作系统启用写时复制且写入频繁,RSS 会明显上升。AOF 重写同样需要 fork,且重写期间的新写入会被缓冲。
复制相关缓冲也要单独看:
redis-cli INFO replication
重点包括 repl-backlog、slave 输出缓冲、复制状态和重新同步次数。如果副本频繁断开重连,backlog 和输出缓冲可能反复增长。给客户端输出缓冲设置限制时,要区分普通客户端、副本和 Pub/Sub 客户端,避免误伤复制链路。
六、客户端缓冲区和无界集合
Redis 不是一个键很大才占内存,大量小键、长列表、Hash、Set、Stream 同样会累积。业务侧要检查队列是否只有生产没有消费、Set 是否无限追加、缓存键是否包含用户 ID 或日期却缺少过期时间。
客户端缓冲可以通过 CLIENT LIST 观察:
redis-cli CLIENT LIST
重点看输出缓冲区、空闲时间和客户端名称。Pub/Sub 或监控工具长时间订阅但没有消费,会积累输出缓冲。生产环境建议给应用连接设置名称,便于定位来源。
哈希、列表、集合的编码阈值也会影响内存。数据量较小时使用紧凑编码,超过阈值后转为标准结构,内存增长呈台阶状。不要为了省内存盲目调高编码阈值,还要同时评估读写延迟和命令复杂度。
七、推荐排查顺序
- 用
INFO memory记录used_memory、RSS、碎片率和 maxmemory。 - 用
INFO keyspace判断键数量和过期键比例。 - 低峰期用
--bigkeys或--memkeys找大键,用MEMORY USAGE确认。 - 检查
maxmemory-policy是否符合数据定位。 - 检查持久化 fork、AOF 重写和复制缓冲。
- 结合业务写入速率、命中率和系统内存曲线判断是否扩容。
扩容前至少保留一份 RDB 或托管备份。对于重要实例,不要直接清空数据;缓存类数据可以先按业务前缀分批删除,观察服务错误率和命中率。
八、验证与预防
处理后要看趋势,不是只看一次数值:
used_memory回到预期区间,或增长速率与业务一致。used_memory_rss与碎片率逐步回落。- 键数量和过期键比例恢复正常。
- 命令延迟、连接数、命中率没有恶化。
- 系统层面没有 swap、OOM 或容器内存重启记录。
预防上,给缓存键统一设置 TTL;为队列和集合设置长度或消费监控;Redis 实例配置内存告警和 maxmemory 告警;持久化与复制缓冲纳入监控;上线前评估大 key 和热 key。清理脚本要先在测试环境验证,并限制 SCAN 批量大小。
结论
Redis 内存升高要先把数据增长、碎片、持久化 fork、复制缓冲和客户端缓冲拆开看。缓存类实例优先检查 TTL 和淘汰策略,存储类实例不要配置 allkeys 淘汰;删除后 RSS 不下降时,先确认 used_memory 是否下降,再考虑碎片整理。数据确实增长时,扩容前完成键归属、大 key 和备份检查,避免只把问题推迟到更大的内存规格。
参考资料
Redis 文档:INFO command
https://redis.io/docs/latest/commands/info/
Redis 文档:MEMORY USAGE
https://redis.io/docs/latest/commands/memory-usage/
Redis 文档:redis-cli –bigkeys
https://redis.io/docs/latest/tools/redis-cli/
Redis 文档:Eviction policy
https://redis.io/docs/latest/develop/reference/eviction/
Redis 文档:Client output buffers
https://redis.io/docs/latest/develop/reference/clients/
Redis 文档:Persistence
https://redis.io/docs/latest/develop/persistence/




