redis的5种基础数据类型中,string仅用sds编码(含5种头结构优化),list/hash/zset根据配置和数据特征在ziplist与quicklist/hashtable/skiplist+dict间切换,zset必须双结构协同实现排序与o(1)查询。

Redis 的 5 种基础数据类型对应哪些底层编码?
Redis 表面有 5 种数据类型(string、list、hash、set、zset),但每种类型背后可能由多种底层编码实现,具体选哪种取决于数据规模、元素大小、是否有序等运行时条件。面试常问“list 什么时候用 ziplist,什么时候切到 quicklist”,本质是在考你是否理解编码切换的触发逻辑。
关键判断依据不是“类型”,而是配置项 + 实际数据特征:
-
list-max-ziplist-size:负数表示按“节点数”限制(如-2表示最多 2 个节点),正数表示按字节限制(如64表示每个元素 ≤64 字节) -
hash-max-ziplist-entries和hash-max-ziplist-value控制hash是否保持为ziplist -
zset-max-ziplist-entries和zset-max-ziplist-value同理影响zset
默认配置下,小数据量的 list/hash/zset 都会优先用 ziplist(内存紧凑),一旦超限就升级为更通用但稍重的结构(如 quicklist、hashtable、skiplist + dict)。
为什么 string 类型没有编码切换?
string 是唯一不涉及多编码切换的基础类型——它只有一种底层实现:redisObject 包裹一个 sds(Simple Dynamic String)。但要注意:sds 本身有 5 种头结构(sdshdr5/8/16/32/64),根据字符串长度自动选择,这属于内存对齐优化,不属于“类型编码切换”范畴。
常见误解是认为 int 编码是独立类型,其实只是 sds 存储纯数字字符串时的隐式优化:当内容能转成 long 且没被其他命令强制转为字符串(如 APPEND),Redis 内部会标记为 REDIS_ENCODING_INT,但对外仍是 string 类型,TYPE 命令返回的永远是 string。
验证方式:
SET key 100 OBJECT ENCODING key // 返回 "int" APPEND key "x" OBJECT ENCODING key // 返回 "raw"
zset 底层为什么需要两个结构?
zset 必须同时支持“按 score 排序”和“快速查 member 是否存在”,单一结构无法兼顾 O(log N) 查找与 O(1) 成员判重。所以 Redis 采用双结构组合:skiplist(跳表)负责排序与范围查询,dict(哈希表)负责 O(1) 的 member 存在性检查。
两者数据完全冗余,但各自不可替代:
- 仅用
skiplist:查某个 member 是否存在要 O(log N),不符合ZSCORE/ZISMEMBER的性能预期 - 仅用
dict:无法按 score 范围(如ZRANGEBYSCORE)高效遍历
注意:zset 的 ziplist 编码是纯内存紧凑格式(score+member 交替排列),不包含跳表或哈希逻辑;一旦升级为 skiplist 编码,就固定使用上述双结构,不再降级。
如何在生产环境观察实际编码?
别依赖文档记忆默认值,直接用 OBJECT ENCODING 和 DEBUG OBJECT 看实时状态:
LPUSH mylist a b c OBJECT ENCODING mylist // 可能返回 "quicklist" 或 "ziplist",取决于配置和元素数量 DEBUG OBJECT mylist // 返回更详细信息,含引用计数、编码、是否 LRU 等
线上慎用 DEBUG 命令(阻塞主线程),推荐先在测试实例验证逻辑;排查慢查询时,若发现某 zset 的 zcard 突然变慢,可检查其编码是否从 ziplist 升级为 skiplist——这不是 bug,是设计使然,但意味着内存占用和 CPU 开销已变化。
真正容易被忽略的是:编码切换发生在写入时(如第 hash-max-ziplist-entries + 1 次 HSET),且不可逆;如果业务数据特征突变(例如 hash 从存 10 个字段变成存 1000 个),旧 key 不会自动重建结构,得靠 RENAME 或重新构造来触发重编码。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










