Redis中的每个Key除了保存用户可见的数据值外,还会记录一组内部属性,例如底层编码方式、序列化后的长度、引用计数和最近访问时间等。TYPE命令只能返回逻辑类型,OBJECT ENCODING命令可以查看当前编码,但DEBUG OBJECT命令能一次性输出更完整的内部信息。理解这些字段有助于判断一个Key是否使用了低效的数据结构,以及是否因为编码升级或内存碎片导致容量异常。

DEBUG OBJECT命令的使用方式非常简单,只需在redis-cli中执行DEBUG OBJECT key即可。虽然它属于DEBUG子命令,看起来更偏向调试场景,但对于日常的内存排查和编码判断也非常实用。该命令会返回一行文本,其中包含多个字段,每个字段都能从不同侧面描述Key的内部状态。
DEBUG OBJECT输出字段详解
先看一个实际执行示例:
127.0.0.1:6379> SET article:title "Redis Debug Object" OK 127.0.0.1:6379> DEBUG OBJECT article:title Value at:0x7f9c3a4b5c60 refcount:1 encoding:embstr serializedlength:23 lru:1837412 lru_seconds_idle:8
这一行输出可以拆成五个主要部分。第一个字段Value at后面跟的是Redis对象在内存中的地址。它是一个十六进制指针,仅用于确认当前进程内对象的位置,不具备跨重启、跨实例比较的意义。第二个字段refcount表示引用计数,对大多数Key来说通常为1。Redis在某些早期版本中会共享小整数对象,例如0到9999之间的整数,因此这些共享对象可能显示大于1的引用计数。不过Redis 4之后共享对象的范围已经大幅收窄,实际业务中看到大于1的概率不高。
第三个字段encoding是最核心的信息,它直接给出当前Key所使用的底层编码。例如同一个字符串内容可能以int、embstr或raw存储,同一个哈希可能以listpack或hashtable存储。第四项serializedlength表示对值进行RDB序列化之后的字节数。它并不等于实际内存占用,因为实际内存还包含Redis对象头、SDS字符串头、dictEntry、指针等额外开销,但序列化长度可以作为一个快速判断值大小的依据。
最后两个字段与访问时间有关。lru是24位时钟值,它记录了该Key最近一次被访问时的分钟级时间刻度,lru_seconds_idle则表示从最近一次访问到现在已经空闲了多少秒。通过这个字段可以快速识别冷数据,例如很多Key的lru_seconds_idle超过几小时甚至几天,说明它们长期没有被读写,可能在容量规划时作为清理候选对象。
常见数据类型的内部编码与切换规则
字符串类型拥有三种典型编码。当一个字符串可以完整表示为64位有符号整数时,Redis会使用int编码直接存整数值,此时不额外分配SDS字符串结构。长度不超过44字节的短字符串通常使用embstr编码,这种编码把Redis对象和SDS字符串分配在同一块连续内存中,减少一次内存分配并提升缓存局部性。超过44字节的字符串则使用raw编码,对象和SDS分开存储。修改一个embstr字符串会使其变为raw,即使最终字符串长度仍然很短,也不会自动回到embstr。
127.0.0.1:6379> SET n 100 OK 127.0.0.1:6379> OBJECT ENCODING n int 127.0.0.1:6379> SET s hello OK 127.0.0.1:6379> OBJECT ENCODING s embstr 127.0.0.1:6379> APPEND s "_world_with_long_content" (integer) 28 127.0.0.1:6379> OBJECT ENCODING s raw
哈希类型在Redis 7以后的版本中使用listpack作为小规模数据的编码方式,较早的Redis版本则使用ziplist。当哈希中的字段数量或单个字段值的长度超过配置阈值时,底层结构会从listpack转换为hashtable。默认情况下,hash-max-listpack-entries为128,hash-max-listpack-value为64。如果哈希中任意字段值超过64字节,或者字段数量超过128个,编码就会升级为hashtable。这种升级是单向的,即使后续删除字段使哈希重新变小,编码也不会自动降回listpack。
127.0.0.1:6379> CONFIG SET hash-max-listpack-entries 512 OK 127.0.0.1:6379> HSET user:1 name abc age 20 (integer) 2 127.0.0.1:6379> OBJECT ENCODING user:1 listpack 127.0.0.1:6379> CONFIG SET hash-max-listpack-entries 1 OK 127.0.0.1:6379> HGETALL user:1 1) "name" 2) "abc" 3) "age" 4) "20" 127.0.0.1:6379> OBJECT ENCODING user:1 hashtable
集合类型同样存在多重编码。当集合中全部是整数并且元素数量不超过set-max-intset-entries时,Redis使用intset编码;整数元素超过该阈值或出现非整数元素后,会先尝试使用listpack,再继续增长则转换为hashtable。有序集合在字段值较短且元素数量较少时使用listpack,超过阈值后转换为skiplist加hashtable的组合结构。列表类型内部由quicklist组织,每个quicklist节点中内嵌多个listpack节点,因此DEBUG OBJECT中看到列表的编码通常为quicklist,而不是直接看到listpack。
用DEBUG OBJECT排查大Key与内存膨胀
当Redis内存增长异常时,首先要做的是找到占用空间最大的Key。虽然redis-cli --bigkeys可以按类型统计大Key,但它基于STRLEN、LLEN等命令,并不能覆盖所有内部编码细节。通过DEBUG OBJECT返回的serializedlength字段,可以更直接地查看每个Key序列化后的字节数,从而估算值本身大小。下面的Python脚本使用SCAN遍历Key,避免KEYS阻塞服务,并输出序列化长度超过阈值的Key。
import redis
r = redis.Redis(decode_responses=True)
threshold = 10240
for key in r.scan_iter(count=1000):
info = r.execute_command('DEBUG', 'OBJECT', key)
encoding = info.split('encoding:')[1].split()[0]
length = int(info.split('serializedlength:')[1].split()[0])
if length > threshold:
print(f"{key} {encoding} {length}")
这个脚本可以帮助快速识别大Key候选,但生产环境使用时需要控制频率。DEBUG OBJECT在计算serializedlength时会对值执行序列化操作,对于包含大量元素的哈希、集合或有序集合来说可能造成短时间的CPU消耗。因此建议在业务低峰期使用SCAN分批执行,并且每次扫描后适当休眠。
除了大Key排查,refcount和lru_seconds_idle也有实际意义。引用计数异常偏高可以提示某些Key被多个地方引用,例如通过共享整数对象或者某些内部缓存机制。空闲时间很长的Key则可能属于冷数据,可以结合maxmemory-policy判断是否应当使用LFU或LRU策略淘汰。注意lru_seconds_idle的单位是秒,但Redis的LRU时钟本身分辨率约为分钟级,因此小于60秒的空闲时间并不能精确反映最近访问的具体秒数。
DEBUG OBJECT的限制与替代方案
DEBUG OBJECT虽然信息丰富,但它并不适合作为生产环境长期频繁使用的监控命令。首先,返回的Value at地址在重启后会变化,无法用于持久化分析。其次,serializedlength是序列化后的大小,可能比实际内存占用小很多。例如一个包含大量小字段的哈希,在listpack编码下序列化长度与内存占用比较接近,但一旦转成hashtable,每个字段都会产生额外的dictEntry、指针和链表开销,导致实际内存远大于序列化长度。仅凭serializedlength可能会低估内存膨胀程度。
更准确的工具是MEMORY USAGE key命令。它返回的值更接近Redis实际为该Key分配的内存量,包括分配器开销。对于嵌套结构,可以追加SAMPLES 0参数进行精确计算,不过在大Key上执行精确计算可能会阻塞较长时间。
127.0.0.1:6379> MEMORY USAGE user:1 (integer) 120 127.0.0.1:6379> OBJECT ENCODING user:1 listpack 127.0.0.1:6379> DEBUG OBJECT user:1 Value at:0x7f9c3a4b5c60 refcount:1 encoding:listpack serializedlength:18 lru:1837412 lru_seconds_idle:5
在实际工作中,可以把DEBUG OBJECT、OBJECT ENCODING和MEMORY USAGE配合使用。OBJECT ENCODING只返回编码名称,适合快速判断当前Key使用了哪种底层结构;DEBUG OBJECT适合查看序列化长度、引用计数和空闲时间;MEMORY USAGE适合评估真实内存占用。三种命令各有侧重,结合起来能够更全面地定位Redis的内存和性能问题。
还需要注意,部分云Redis服务出于安全考虑禁用了DEBUG命令,此时只能依赖OBJECT ENCODING和MEMORY USAGE。另外,DEBUG OBJECT属于调试子命令,Redis官方并不保证其输出格式在所有版本中完全不变,自动化解析时应考虑版本差异,例如Redis 7将ziplist编码替换为listpack后,返回值已经发生了变化。
Redis DEBUG OBJECT内部编码Key编码修改时间:2026-08-30 21:30:23