在Redis里,一个键的value并不仅仅是一串字节。Redis使用redisObject结构对每个值进行包装,type字段描述逻辑类型,encoding字段描述实际存储方式,也就是内部编码。相同逻辑类型可以有多种内部编码,比如字符串可以保存为整数、嵌入式字符串或原始SDS,哈希可以用紧凑型listpack或标准hashtable。编码选择取决于值的大小和配置阈值,直接影响CPU和内存。要查看某个键当前采用哪种内部编码,通常先想到object encoding key,但要获得序列化长度、引用计数等更详细信息,就需要debug object key。

一、Redis对象内部编码是什么
Redis的键值存储并不是简单地把字符串直接放进字典。每个value都会被打包为一个redisObject结构,内部包含类型、编码、引用计数、LRU时钟以及数据指针。类型的取值范围是Redis五种基础类型中的一种,而编码则负责回答这个对象到底用哪种内存布局落地。举例来说,一个短字符串可以用连续内存的embstr编码保存,一个纯数字字符串可能直接变成int编码,而长字符串只能使用raw编码。
这种设计让Redis可以在小对象阶段采用紧凑结构,减少malloc次数和指针跳转的开销,同时保留向大结构切换的能力。比如哈希在字段很少且值很短时会使用listpack,一旦字段数量或值长度超过阈值,就自动重建为hashtable。编码转换通常发生在数据修改操作中,例如追加字符串、增加哈希字段或删除集合元素。
对开发者来说,内部编码的价值在于解释一些反直觉的现象:明明只存了三个字段,内存却多了几倍;或者明明值不变,延迟却突然上升。掌握debug object命令后,可以快速确认编码状态,为参数调整提供依据。
二、DEBUG OBJECT命令详解
DEBUG OBJECT命令的语法很简单,只要传入一个存在的键名即可。执行后会打印若干字段,其中最重要的是encoding和serializedlength。下面是一个典型输出:
127.0.0.1:6379> set foo bar OK 127.0.0.1:6379> debug object foo Value at:0x7f2c4c0d0d60 refcount:1 encoding:raw serializedlength:7 lru:16613207 lru_seconds_idle:15
上述输出中,Value at表示对象在内存中的地址,refcount是引用计数,encoding是当前内部编码,serializedlength是序列化后的字节长度,lru和lru_seconds_idle分别表示最后访问时钟和空闲秒数。serializedlength并不等于真实内存占用,它更像是把值序列化后得到的逻辑字节数,但依然有参考价值。
对于哈希、列表等复合类型,DEBUG OBJECT同样可以查看编码。例如一个只包含两个字段的小哈希通常显示为listpack:
127.0.0.1:6379> hset user:1 name Alice age 30 (integer) 2 127.0.0.1:6379> debug object user:1 Value at:0x7f2c4c0d0e50 refcount:1 encoding:listpack serializedlength:41 lru:16613208 lru_seconds_idle:5
需要注意的是,DEBUG OBJECT并不适合频繁调用。为了计算serializedlength,Redis需要遍历对象内容,对于包含大量元素的列表、哈希或集合,这可能造成主线程阻塞。所以该命令更适合在测试环境或低峰期执行。线上排查时可以先使用object encoding key获取编码,那个命令是O(1)级别,几乎无阻塞风险。
三、常见内部编码及转换条件
不同Redis版本对内部编码的支持有差异。Redis 7以后,ziplist逐渐被listpack替代,但核心思想一致:小对象紧凑存储,大对象切换到标准结构。字符串类型最常见的三种编码是int、embstr和raw。当字符串能解析为整数且不超过64位有符号整数范围时,Redis会直接使用int编码;当字符串长度较短(通常不超过44字节)时用embstr,一次性分配连续内存;再长则切成raw编码。
哈希、有序集合以及集合的编码选择与元素数量和单个元素长度有关。以哈希为例,默认当字段数量不超过128且所有字段和值的字符串长度都不超过64字节时,使用listpack编码。一旦超出任一条件,Redis会转换为hashtable。可以用以下命令观察阈值触发:
127.0.0.1:6379> config set hash-max-listpack-entries 1 OK 127.0.0.1:6379> hset h1 f1 v1 (integer) 1 127.0.0.1:6379> debug object h1 Value at:0x7f2c4c0d1000 refcount:1 encoding:hashtable serializedlength:25 lru:16613209 lru_seconds_idle:3
列表在Redis 7中统一使用quicklist,它由多个listpack节点组成,配置项list-max-listpack-size控制每个节点的元素数上限。集合在元素全为整数且数量不超过set-max-intset-entries时使用intset,否则切换为hashtable。有序集合则默认使用listpack编码,当成员数量或成员值长度超过zset-max-listpack-entries和zset-max-listpack-value时切换为skiplist加hashtable的组合结构。
这些配置可以通过config get和config set实时调整,但调整后只对新建或重新编码的对象生效,已有对象不会立即改变。想要强制低内存占用,可以在修改配置后通过复制键、删除原键再改名等方式让数据按新阈值重新构建。
四、内存优化实战与注意事项
排查内存占用时,debug object给出的encoding字段能快速判断对象是否处于紧凑编码。例如一个十万元素的有序集合,如果encoding显示skiplist,那就比listpack编码多出不少指针和索引开销。但此时不能盲目调大阈值,因为listpack在元素较多时读取和更新复杂度会上升,可能影响性能。真正合理的做法是结合业务访问模式,比如高频读取且元素很多时保留skiplist,低频且元素较小时尽量使用listpack。
日常检查推荐使用object encoding命令替代debug object。它只返回编码名称,不会扫描值内容:
127.0.0.1:6379> object encoding user:1 "listpack" 127.0.0.1:6379> memory usage user:1 (integer) 72
memory usage命令则给出更接近实际分配的内存字节数,两者配合可以大致评估优化空间。需要注意的是,memory usage对大数据结构也可能产生一定采样开销,生产环境应避免在高频循环中调用。
最后再强调一点,DEBUG OBJECT并不是万能的。它最早定位是调试工具,部分Redis云服务出于安全考虑会禁用它。遇到权限受限或命令被隐藏时,可以退而使用object encoding和memory usage。理解内部编码的本质,才是优化Redis内存和延迟的起点。
Redis对象内部编码debug object修改时间:2026-09-25 23:06:02