登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  Redis

Redis 过期后内存未下降 怎样处理

来源:17golang原创

时间:2026-10-22 06:12:07 380浏览 收藏

我遇到过一种很容易误判的 Redis 内存告警:一批带 TTL 的缓存已经过期,业务读取也拿不到它们,但进程的 RSS 几乎没有下降。这个现象不一定说明过期失效,通常要把“键是否已经从逻辑数据集消失”“Redis allocator 是否复用了空闲块”“操作系统是否收回页面”拆开看。

官方地址:https://redis.io/

先给结论:先看 used_memory 是否下降,再看 used_memory_rss。前者下降、后者不变,多半是分配器碎片或峰值页面暂未归还;两者都不降,才继续排查过期清理、仍在增长的键和后台释放队列。

先把过期键和进程内存分开看

Redis 的过期不是“到点立刻让进程向操作系统归还同等大小内存”。键被访问时可以触发惰性清理,后台也会增量扫描过期键。即使键已经被删除,分配器仍可能保留页面,等待后续写入复用。

# 先看单个 key 的逻辑状态;-2 表示 key 不存在,-1 表示没有 TTL
redis-cli TTL cache:item:42
redis-cli EXISTS cache:item:42

# 只读取内存字段,不用 KEYS 扫描生产实例
redis-cli INFO memory | egrep 'used_memory:|used_memory_rss:|allocator_frag_ratio:|allocator_resident:|lazyfree_pending_objects:'

如果 TTL 已经返回 -2,但 RSS 不动,先不要把问题定性为“Redis 没删”。如果业务使用的是集合、哈希字段或模块索引,也要确认你设置 TTL 的对象确实是顶层 key;字段级数据不能简单套用顶层 key 的判断。

Redis 过期时间、清理机制、分配器页面和进程 RSS 的静态关系说明图
图1:Redis 过期生命周期与内存观测的结构说明图,展示逻辑过期、分配器页面和 RSS 的关系;这是静态说明图,不是运行截图。

用 INFO memory 判断到底是哪一层没下降

我通常先做一个很朴素的对比:used_memory 更接近 Redis allocator 已分配给数据和内部结构的用量,used_memory_rss 是操作系统看到的常驻页面。删除键后前者下降、后者维持高位,往往是 allocator 还握着可复用页面;这时 RSS 高不等于还有同样多的活跃键。

观察组合优先判断下一步
used_memory 不降键未按预期清掉,或新写入抵消了释放抽查 TTL、写入量和 keyspace,确认是否持续生成新 key
used_memory 降,RSS 不降分配器碎片、峰值页面或 RSS 额外开销看 allocator_frag_ratio、allocator_resident,再选择整理动作
lazyfree_pending_objects 持续升高后台异步释放跟不上生产速度先观察队列和 CPU,再决定是否调整大对象过期策略

这里不要只盯着 mem_fragmentation_ratio 一个比值。Redis 官方文档还提供 allocator_frag_ratio、allocator_frag_bytes、allocator_resident 等更细的分层指标;小幅差异或经历过很高峰值时,比值本身可能会放大感受。

Redis 内存指标与 active defrag、MEMORY PURGE、maxmemory 处理动作的静态关系说明图
图2:Redis 内存指标与处理动作的关系说明图,帮助读者按指标差异选择动作;这是静态说明图,不是运行截图。

按证据选择处理动作

如果只是 RSS 没下降,但 used_memory 已经回落,先让 allocator 复用这部分空间,不要为了一个高 RSS 立刻重启。若确认碎片长期偏高且 CPU 有余量,可以评估 active defragmentation;它是持续的 CPU 换内存整理,不是免费的“立即清空”。

# 先读取配置,确认实例是否支持并启用了主动碎片整理
redis-cli CONFIG GET activedefrag active-defrag-threshold-lower active-defrag-threshold-upper

# 仅在确认 CPU 余量、变更已审批时启用;不要把它当作过期修复按钮
redis-cli CONFIG SET activedefrag yes

# jemalloc 下尝试把可释放的脏页交还给 allocator;其他 allocator 可能是 NOOP
redis-cli MEMORY PURGE

如果问题是大对象过期释放造成主线程抖动,可以评估 lazyfree-lazy-expire yes,但这会把释放转移到后台,短时间内要关注 lazyfree_pending_objects,它也不会保证 RSS 立刻下降。缓存场景还应设置合理的 maxmemory 和淘汰策略,按峰值、碎片和复制/AOF 等额外开销留出余量。

把排查动作沉淀成落地清单

我最后会把这件事收敛成四个固定动作:写入路径统一设置 TTL,并记录没有 TTL 的异常 key;监控同时采集 used_memory、used_memory_rss、allocator_frag_bytes 和 lazyfree_pending_objects;容量按峰值而不是某个平稳时刻估算;变更 active defrag、异步过期或 purge 前,先确认 CPU、复制、AOF 和回滚方案。

Redis 过期后“内存没下降”真正要回答的不是一个数字有没有马上变小,而是:数据是否已经释放、释放后的空间是否可复用、页面是否需要归还操作系统。按这个层次看,误报会少很多,真正的容量问题也更容易定位。

常见问题

TTL 到期后,为什么 DBSIZE 还可能暂时不变?

过期键会在访问时或后台增量清理时删除,TTL 到零与删除动作不是同一个瞬间。观察时要结合抽查结果和内存指标,不要只用一个瞬时数量下结论。

MEMORY PURGE 能不能直接解决内存泄漏?

不能。它处理的是 allocator 可以回收的页面,不能替代排查仍在增长的 key、没有 TTL 的数据或业务重复写入;而且该命令只在 jemalloc 下真正实现,执行也可能较慢。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>