memory usage返回的是jemalloc实际分配的内存总量,包含键名、值、redisobject元数据、过期字段、底层容器开销及内存对齐填充等,不等于数据本身大小,也不区分是否过期或共享。

MEMORY USAGE 返回的不是“数据大小”,而是运行时真实分配量
MEMORY USAGE 返回的是 jemalloc 实际分配的字节数,包含键名、值数据、redisObject 元数据、过期时间字段、底层容器(如 dict / ziplist)的指针与空桶开销、内存对齐填充等。它不等于 strlen(value),也不等于序列化后的长度。
常见误判场景:
- 一个存了 100 个字段的
hash,VALUE总长才 2KB,但MEMORY USAGE返回 1.8MB —— 很可能因hash-max-ziplist-entries超限,已切换为hashtable编码,且哈希表扩容到 65536 桶,每个dictEntry占 32 字节以上 -
set中存 100 万个字符串,MEMORY USAGE显示 120MB,但INFO memory的used_memory增量是 158MB —— 差的那部分来自 jemalloc 分配块粒度(如 128 字节对齐)和dict结构体自身开销(96 字节固定) - 对已过期但未被删除的 key 执行
MEMORY USAGE,仍返回完整值 —— 它不区分是否有效,只看内存是否还被持有
为什么不能用 MEMORY USAGE 直接统计“hash 占比”或“zset 总用量”
MEMORY USAGE 不返回类型信息,也不支持批量或通配符。你无法靠它直接回答“当前 DB 中 hash 类型占用了多少内存”。
必须组合三步才能归类统计:
- 先用
SCAN游标遍历 key(避免阻塞,禁用KEYS *) - 对每个 key 执行
TYPE获取结构类型 - 再执行
MEMORY USAGE获取其字节数,本地聚合
注意:MEMORY USAGE 对不存在的 key 返回 (integer) 0,若脚本未校验 key 存在性,会把大量 miss 当成“零占用”混入统计,拉低平均值;集群环境下还需手动路由到对应节点,否则报 CROSSSLOT 错误。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
不同数据类型的 MEMORY USAGE 行为差异
同一命令在不同结构上计算逻辑不同,直接影响结果可信度:
-
string:最接近“真实值”,主要含键名 + 值 +redisObject(16 字节)+ 过期字段(8 字节),无额外容器开销 -
hash/zset/set:取决于编码。用DEBUG OBJECT key查encoding字段:ziplist或intset时内存紧凑;一旦切到hashtable或skiplist,开销陡增,且MEMORY USAGE会反映整个结构体分配量 -
list:Redis 7+ 默认用listpack,但若元素过多或含大 value,会转quicklist;此时MEMORY USAGE包含所有listNode指针 +listpack块分配 + 冗余空间 -
SAMPLES参数仅对set/zset/hash生效(默认采样 5 个元素),对string或list无效 —— 传了也忽略
生产环境该怎么做才不翻车
别在高峰批量扫 key。一次调用 MEMORY USAGE 可能阻塞主线程,尤其对百万级 zset 或嵌套深的 hash。
更稳的路径是分层验证:
- 先跑
redis-cli --bigkeys -i 0.1,快速识别各类型 top 样本(比如hash: 421 keys, avg size 3.2MB) - 挑出前 3 个最大
hashkey,逐个执行MEMORY USAGE+HLEN+DEBUG OBJECT,确认是否真由字段膨胀导致,还是编码切换所致 - 查
INFO memory中used_memory_human和DBSIZE比值:若 DBSIZE 是 80 万但内存已达 12GB,基本可断定存在少数几个“胖 key”,不用全量扫描 - 想导出全量分布?只在开发环境用
SCAN+TYPE+MEMORY USAGE脚本,并设好client-output-buffer-limit normal 256mb 128mb 60防断连
最易被忽略的一点:它不体现共享对象。同一个字符串 value 被 10 个 key 引用,MEMORY USAGE 会算 10 次 —— 但实际内存只存一份。这点在分析 refcount > 1 的冷 key 时尤其误导。










