在排查 Redis 内存占用问题时,很多同学习惯直接执行 INFO memory,然后盯着 used_memory 看。这个方法能知道当前进程用了多少内存,但一旦遇到内存异常增长、内存碎片偏高或者实例实际占用比预想大很多,单看总量指标往往很难找到原因。Redis 从 4.0 开始提供了 MEMORY STATS 命令,它把内存统计拆得更细,可以看到分配器内部状态、数据集真实大小、管理开销、Lua 脚本缓存等不同维度的占用情况。这篇文章就围绕 MEMORY STATS 展开,说明它的输出结构、关键指标含义,以及如何用它配合 INFO memory 做内存分析。

MEMORY STATS 命令基础与输出结构
在 redis-cli 中直接执行 MEMORY STATS,会得到一组类似 JSON 结构的数据。这个命令不需要额外参数,返回信息比 INFO memory 更偏向内存分配器的视角。比如一个简单实例的输出片段如下:
127.0.0.1:6379> MEMORY STATS 1) "peak.allocated" 2) (integer) 1048576 3) "total.allocated" 4) (integer) 524288 5) "startup.allocated" 6) (integer) 897712 7) "replication.backlog" 8) (integer) 1048576 9) "clients.slaves" 10) (integer) 0 11) "clients.normal" 12) (integer) 49616 13) "aof.buffer" 14) (integer) 0 15) "lua.caches" 16) (integer) 0 17) "overhead.total" 18) (integer) 1995904 19) "keys.count" 20) (integer) 1 21) "keys.bytes-per-key" 22) (integer) 87381 23) "dataset.bytes" 24) (integer) 87381 25) "dataset.percentage" 26) (float) 4.19 27) "peak.percentage" 28) (float) 50 29) "allocator.allocated" 30) (integer) 542997 31) "allocator.active" 32) (integer) 1048576 33) "allocator.resident" 34) (integer) 1048576 35) "fragmentation" 36) (float) 1.93 37) "fragmentation.bytes" 38) (integer) 505579
这里只截取了一部分字段,但已经能看出 MEMORY STATS 把内存拆成了几个组:以 peak、total、startup、replication.backlog、clients、lua.caches、aof.buffer 开头的指标,描述的是 Redis 内部各模块占用的内存;而 allocator 系列指标,描述的是底层内存分配器,例如 jemalloc 或 libc malloc 的状态;dataset 系列则专门描述用户键值数据占用的内存。
理解这种分组关系很重要。比如 total.allocated 是 Redis 认为自己已经分配出去的内存,它与 allocator.allocated 并不完全相同,因为分配器自身有对齐、元数据以及碎片等额外开销。因此在分析时不能只盯某一个字段,而要结合 allocator 与 dataset 一起看。
核心字段解读:分配器、碎片与数据集
先看分配器相关的几个指标。以 jemalloc 为例,allocator.allocated 表示 Redis 通过分配器申请到的、正在使用中的内存字节数,allocator.active 是分配器为了满足这些请求实际保留的活跃内存页大小,而 allocator.resident 是分配器在操作系统层面实际驻留的物理内存。一般来说,allocator.active 会大于或者等于 allocator.allocated,因为分配器不可能每次申请都刚好用完一整页。
碎片率主要看 fragmentation 和 fragmentation.bytes。在 INFO memory 中也有 mem_fragmentation_ratio,它的计算方式是 used_memory_rss / used_memory。而 MEMORY STATS 中的 fragmentation 则是从分配器内部视角计算出来的,通常由 allocator.active / allocator.allocated 得到。如果一个实例的 fragmentation 持续高于 1.5,同时 fragmentation.bytes 数值也很大,说明分配器内部存在较多浪费,可能需要考虑启用 jemalloc 的主动碎片整理,或者重启实例来回收内存。
数据集相关的 dataset.bytes 表示用户数据实际占用的内存,也就是所有键和值经过序列化之后的总大小。keys.count 是当前键的总数,keys.bytes-per-key 是平均每个键分摊到的内存字节数,这个值在对比不同业务 Redis 实例时比较有用。dataset.percentage 表示数据集占 total.allocated 的百分比。如果这个比例较低,说明大量内存没有被用户数据使用,而是被复制积压、客户端缓冲区、脚本缓存或者其他管理开销占用了。
还有一个容易被忽略的指标是 overhead.total,它汇总了 Redis 为了维护键值数据之外的功能所消耗的内存,包含复制积压缓冲区、客户端输出缓冲区、AOF 缓冲区、Lua 脚本缓存等等。在大 key 很多或者主从复制频繁的场景下,overhead.total 可能比 dataset.bytes 还要高,这时只清理数据并不能显著降低内存占用,反而需要检查客户端连接、复制状态和脚本使用情况。
MEMORY STATS 与 INFO memory 的字段对比
很多 Redis 用户对 INFO memory 里的字段更熟悉,例如 used_memory、used_memory_human、used_memory_rss、used_memory_peak、used_memory_lua、mem_fragmentation_ratio 等。MEMORY STATS 并不是要替代 INFO memory,而是从另一个角度补充更细的信息。理解两者的对应关系,可以更快定位内存问题。
比如 used_memory 对应的是 total.allocated,表示 Redis 内部统计的所有已分配内存总量。used_memory_rss 对应 allocator.resident 的一部分,不过 rss 还包括进程其他内存段,所以两者不完全等价。used_memory_peak 对应 peak.allocated,used_memory_lua 在较新版本中对应 lua.caches。当你在 INFO memory 里看到 used_memory 很高,但业务数据量没有增加时,可以马上执行 MEMORY STATS,重点看 overhead.total 下各个子项,找到是复制积压、客户端还是脚本导致的内存占用。
另一个典型的场景是主从复制。主节点会为每个从节点维护一个复制积压缓冲区,默认大小由 repl-backlog-size 控制。这个大小不会因为从节点消费慢而自动缩小,所以从节点很多或者网络抖动频繁时,replication.backlog 可能会占用大量内存。通过 MEMORY STATS 可以看到这一项的具体字节数,再结合 INFO replication 查看从节点数量和积压情况,就能判断是否需要调整 repl-backlog-size 参数。
用 MEMORY STATS 排查内存问题的实践思路
假设线上一个 Redis 实例的总内存持续逼近 maxmemory,但键数量并没有明显增长。常规思路是先执行 INFO memory,如果发现 used_memory 明显小于 used_memory_rss,说明碎片较高;如果两者接近,但 used_memory 仍然很大,就需要进一步拆分内部占用。这时可以执行 MEMORY STATS,按照以下顺序检查。
第一步看 dataset.bytes 和 overhead.total。如果 dataset.bytes 占比很高,说明内存主要花在业务数据上,下一步需要使用 redis-cli --bigkeys 或者 MEMORY USAGE key 找出大 key。如果 overhead.total 占比很高,则继续查看 clients.normal、clients.slaves、replication.backlog、lua.caches 等子项,定位具体原因。
第二步看 fragmentation 和 allocator.active。如果碎片率超过 1.5,尤其是达到 2 以上,并且 fragmentation.bytes 占据了几百 MB 甚至更多,说明分配器内部浪费严重。此时可以考虑把 activedefrag 参数打开,让 Redis 在后台自动整理碎片。当然,启用主动碎片整理会消耗一定的 CPU,需要在内存和 CPU 之间权衡。
第三步结合 peak.allocated 和 peak.percentage 判断内存峰值是否合理。如果当前内存只有峰值的 50%,但实例仍然被限制在最大内存附近,可能是某些已经释放的内存没有真正归还给操作系统,或者 maxmemory-policy 触发过淘汰后又重新写入。这类问题需要结合业务写入模式和 key 过期策略一起分析,单看瞬时统计无法得出结论。
此外,如果使用了 Lua 脚本,并且脚本执行过程中会向 Redis 写入数据,那么 lua.caches 可能会慢慢增长。通过 MEMORY STATS 能直接看到 Lua 脚本缓存占用的字节数,再执行 SCRIPT FLUSH 可以清空脚本缓存,但要注意线上环境需要确认脚本调用方是否会自动重新加载。
实战示例:计算内部各模块内存占比
下面给出一段简单的 shell 命令,用来把 MEMORY STATS 中几个关键字段提取出来,方便观察内存分布在哪些模块。
redis-cli MEMORY STATS | grep -E "dataset.bytes|overhead.total|replication.backlog|clients.normal|lua.caches|fragmentation$|allocator.allocated|allocator.active"
在实际定位问题时,如果输出类似下面这样:
1) "dataset.bytes" 2) (integer) 104857600 3) "overhead.total" 4) (integer) 52428800 5) "replication.backlog" 6) (integer) 16777216 7) "clients.normal" 8) (integer) 2097152 9) "lua.caches" 10) (integer) 1048576 11) "fragmentation" 12) (float) 1.12 13) "allocator.allocated" 14) (integer) 157286400 15) "allocator.active" 16) (integer) 176160768
从这些数据可以看出,数据集占据 100 MB,管理开销约 50 MB,其中复制积压约 16 MB,客户端普通连接约 2 MB,Lua 缓存约 1 MB。碎片率只有 1.12,说明碎片问题不大,内存压力主要来自数据本身和复制积压。此时可以优先检查是否存在大 key 或长期未过期的数据,同时评估 repl-backlog-size 是否需要调小。
如果换成另一种情况,dataset.bytes 只有 20 MB,但 overhead.total 高达 150 MB,并且 clients.normal 也接近 100 MB,那就要怀疑是否存在大量普通客户端长期订阅通道或者输出缓冲区积压。比如某个消费者处理速度很慢,导致 Redis 为该客户端分配的输出缓冲区不断增长,最终拖垮整个实例。此时通过 CLIENT LIST 查看 omem 字段,可以找到占用内存最大的客户端连接。
需要注意的是,MEMORY STATS 的输出在不同 Redis 版本中字段会略有差异,比如 Redis 7 之后增加了 functions.caches 和 aof.buffer 等更细的字段。因此分析时最好结合当前版本的 INFO memory 和官方文档,不要机械套用旧版本字段映射。
小结
MEMORY STATS 是 Redis 内存分析中一个非常实用的命令。它把内存从分配器、数据集、复制积压、客户端、Lua 缓存等维度拆开,弥补了 INFO memory 只有总量指标的不足。在排查内存占用高、碎片率高、主从复制内存异常等场景时,先看 dataset.bytes 和 overhead.total 确定大方向,再看 fragmentation 和各个子模块,可以快速缩小问题范围。配合 MEMORY USAGE、MEMORY DOCTOR 以及 CLIENT LIST 等命令,能够更全面地掌握 Redis 实例的内存健康状况,避免盲目扩容或重启。
Redis内存统计MEMORY STATS内存碎片率修改时间:2026-10-04 00:14:24