Redis 内存一直涨,先别急着怪大 key,先看涨的是哪一段
Redis 内存上涨时,现场最常见的误判就是一上来就找大 key 或直接扩容。真正更值钱的第一步,是先分清涨的是数据集、RSS,还是客户端相关内存。
Redis 内存一往上走,现场十有八九会先冒出一句话:
- 是不是大 key 了?
这句话不能说错,但它经常把排查带偏。
因为很多时候,大家嘴里说的是“大 key”,心里想的是“赶紧找到一个明确坏点”,可 Redis 内存上涨这件事,本来就不一定是同一类问题。
有时真的是数据集在变大。
有时是 used_memory 没涨多少,RSS 却在往上爬。
还有时根本不是业务 key,而是客户端输出缓冲、复制积压或者 fork 带出来的额外占用。
如果这几段不先拆开,后面排查很容易一路跑偏:
- 把 RSS 当成业务数据膨胀
- 把客户端缓冲当成 key 变多
- 把碎片率高误判成必须立刻删数据
- 最后不是问题没解决,就是做了一堆伤害性动作
所以我现在看 Redis 内存上涨,第一反应反而不是“先找大 key”,而是另一句更朴素的话:
先别急着下结论,先看这次到底是哪一段在涨。
这篇文章就只想把这一条线写清楚。
最常见的误判:看到内存涨,就默认是缓存数据失控
这个误判为什么常见?因为监控图通常先给人的就是一个总量数字。
内存从 60% 到 75%,再到 85%,大家天然会把它理解成“缓存里的东西越来越多了”。
但 Redis 的“内存涨了”,至少可能有三种完全不同的现场:
第一种:数据集真的在涨
这时更像是:
- 新 key 持续写入
- 旧 key 没有按预期退出
- TTL 设计有问题
- 某类集合或对象规模在膨胀
这一类才接近大家直觉里的“缓存真的装太多了”。
第二种:业务数据没涨多少,但 RSS 在涨
这时常见原因反而是:
- 内存碎片率高
- 大量写删带来的 allocator 回收不理想
- fork 期间 copy-on-write 把 RSS 放大
- active defrag 没跟上
这种场景下,如果你只是盯着“总内存占用变大”,很容易以为是 key 变多了,实际可能并不是。
第三种:涨的不是数据,而是客户端和复制相关占用
这类问题在值班现场特别容易被忽略,因为它不像业务 key 那么直观。
比如:
- 慢消费者导致输出缓冲堆积
- 从库复制链路积压
- 某类客户端连接方式异常
- Pub/Sub 或订阅类使用方式把内存顶上去
这时候你如果一头扎进 key 采样,往往半天都看不出为什么“数据没多多少,内存却还是在涨”。
所以第一步只做一件事:把上涨落到正确位置
如果现场已经开始紧张,我通常会先对着几组指标把问题收窄,而不是立刻展开一整套大而全排查。
最先看的其实就几项:
used_memoryused_memory_rssmem_clients_normalmem_clients_slavesexpired_keysevicted_keys
这几项不复杂,但足够把方向拉直。
如果 used_memory 跟着明显上涨
那就先承认:这次更像数据集本身在变大。
这时候重点再回到:
- key 数量是不是在涨
- 是否大量 key 没有 TTL
- TTL 是不是过长
- 某类集合、列表、Hash 是否在持续膨胀
- 有没有少数大 key 把内存集中吃掉
也就是说,只有当你确认 used_memory 真在涨时,“去找大 key”“去看 TTL 设计”这些动作才是真正同一条线上。
如果 used_memory 还算平,但 RSS 一直涨
那就别先把矛头指向业务数据了。
这更像:
- 内存碎片
- fork 期间额外放大
- 大量写删后的回收效率问题
这种情况下,现场最容易做错的一件事就是:看见总内存高,就急着清缓存。
但如果业务数据本身没怎么涨,清缓存不一定能把 RSS 按预期打下来,反而可能把命中率和回源链搞坏。
如果客户端相关内存也在同步长
那就得把视线从 key 本身挪开一点。
这类现场更该优先问的是:
- 有没有慢消费者
- 有没有连接堆积
- 主从复制是不是异常拉长了缓冲占用
- 某类客户端是不是读写方式不对
因为这时内存上涨的主线,可能根本不在业务缓存设计,而在使用方式。
我更在意的,不是“有没有大 key”,而是“旧数据为什么没退下去”
就算最后确认是数据集在涨,我也不太喜欢一上来就把问题收成“大 key 排查”。
因为很多 Redis 内存上涨,根因其实不是某个特别夸张的单 key,而是更普通、也更难被第一眼看出来的问题:
- key 数量一直在加
- 过期策略名义上有,实际上退得很慢
- 某些写路径漏了 TTL
- 活动、预热、灰度、兜底缓存一层层叠上去,没人再说得清哪些 key 会自然消失
也就是说,真正更值得先问的是:
这些本该是缓存的数据,为什么没有按预期退场?
这个问题一旦问清,很多现场会比“先扫大 key”收得更快。
因为大 key 只是少数对象的问题,旧数据退不掉才更像系统性问题。
一个更接近值班现场的判断顺序
以后如果再碰到 Redis 内存持续上涨,我会更建议按下面这个顺序收:
先分清涨的是数据、RSS,还是客户端相关占用
这是第一刀。没这一步,后面全靠猜。
再确认是不是“新数据一直进,旧数据退不掉”
如果 used_memory 在涨,这一步往往最关键。重点不是先追究某个神秘坏 key,而是先看 key 总量、TTL 分布、过期是否真的在发生。
然后才看是否存在少数大 key 或局部膨胀对象
这一步当然要做,但它更像第二轮收口动作,不该永远抢在最前面。
最后再决定是扩容、清理、调 TTL,还是修使用方式
很多团队把动作顺序做反了:先扩容,先删数据,先清缓存,最后才去理解到底哪段在涨。
这样做有时能暂时止血,但不容易留下真正有用的判断。
为什么这条线比“大而全分型”更重要
因为值班现场没有那么多耐心看一套完整分类学。
真正能救人的,通常只是一个很短的判断线:
- 先别急着喊大 key
- 先看涨的是哪一段
- 再决定问题是在数据、RSS,还是客户端相关内存
- 再往下做对应动作
这条线看起来简单,但它能避免很多特别常见的误判。
而 Redis 内存问题最怕的,恰好就不是“暂时还没完全定位”,而是“定位方向一开始就错了”。
最后压成一句话
Redis 内存上涨时,最先要避免的不是漏看某个大 key,而是把所有上涨都当成同一种上涨。
先分清涨的是哪一段,再决定往哪条线追,这一步比后面很多技巧都值钱。
因为只有方向对了,你才知道自己是在处理缓存数据、处理内存形态,还是在处理客户端和复制链路。