Redis对象编码与内存优化
约 1621 字大约 5 分钟
redisencoding
2025-05-07
Redis 中每个键值对都以 RedisObject 的形式存储,不同数据类型在不同条件下采用不同的底层编码。理解编码机制是优化 Redis 内存使用的基础。
RedisObject 结构
每个 Redis 值都是一个 redisObject 结构体:
typedef struct redisObject {
unsigned type:4; // 数据类型 (string/list/hash/set/zset)
unsigned encoding:4; // 底层编码
unsigned lru:LRU_BITS; // LRU 时间或 LFU 频率
int refcount; // 引用计数
void *ptr; // 指向底层数据结构的指针
} robj;数据类型与编码对应关系
String 编码
int 编码
当字符串内容是 64 位以内的整数时,Redis 直接将整数值存储在 ptr 指针中,无需额外分配内存。
127.0.0.1:6379> SET counter 12345
OK
127.0.0.1:6379> OBJECT ENCODING counter
"int"
# 共享整数对象:0-9999 的整数对象预先创建并共享
# 减少内存分配embstr 编码
当字符串长度不超过 44 字节时,使用 embstr 编码。redisObject 和 SDS(Simple Dynamic String) 在一块连续内存中分配。
127.0.0.1:6379> SET short "hello"
OK
127.0.0.1:6379> OBJECT ENCODING short
"embstr"
# 超过 44 字节自动转为 raw
127.0.0.1:6379> SET long "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
OK
127.0.0.1:6379> OBJECT ENCODING long
"raw"embstr vs raw:embstr 只需一次内存分配(cache friendly),但是只读的——任何修改操作(APPEND 等)都会转为 raw。
编码转换规则
List 编码
listpack (Redis 7.0+) / ziplist (旧版)
紧凑的连续内存结构,适合少量小元素。
127.0.0.1:6379> RPUSH mylist a b c
(integer) 3
127.0.0.1:6379> OBJECT ENCODING mylist
"listpack" # Redis 7.0+ / "ziplist" in older versionsquicklist
双向链表 + listpack/ziplist 节点的混合结构,兼顾内存和性能。
# 每个 quicklist 节点最大 listpack 大小
# -1: 4KB, -2: 8KB (默认), -3: 16KB, -4: 32KB, -5: 64KB
CONFIG SET list-max-listpack-size -2
# 两端不压缩的节点数(中间节点可以 LZF 压缩)
CONFIG SET list-compress-depth 1转换阈值:
# Redis 7.0+
CONFIG SET list-max-listpack-size -2 # 单节点最大 8KBHash 编码
listpack / ziplist 编码
当字段数量少且值不大时,Hash 使用 listpack 存储(连续内存,key-value 交替存放)。
127.0.0.1:6379> HSET user name "Alice" age "25"
(integer) 2
127.0.0.1:6379> OBJECT ENCODING user
"listpack"转换为 hashtable 的条件
# 字段数量超过阈值 → hashtable
CONFIG SET hash-max-listpack-entries 128 # 默认 128
# 单个字段/值长度超过阈值 → hashtable
CONFIG SET hash-max-listpack-value 64 # 默认 64 字节Set 编码
intset 编码
当集合中所有元素都是整数,且数量较少时使用 intset。intset 是有序整数数组,支持 16/32/64 位自动升级。
127.0.0.1:6379> SADD nums 1 2 3 4 5
(integer) 5
127.0.0.1:6379> OBJECT ENCODING nums
"intset"
# 加入非整数元素 → 转为 hashtable/listpack
127.0.0.1:6379> SADD nums "hello"
(integer) 1
127.0.0.1:6379> OBJECT ENCODING nums
"hashtable"转换阈值
# intset 元素数上限
CONFIG SET set-max-intset-entries 512 # 默认 512
# Redis 7.2+ 支持 listpack 编码
CONFIG SET set-max-listpack-entries 128
CONFIG SET set-max-listpack-value 64Sorted Set 编码
listpack / ziplist 编码
元素少且值小时使用紧凑编码。
127.0.0.1:6379> ZADD rank 100 "alice" 200 "bob"
(integer) 2
127.0.0.1:6379> OBJECT ENCODING rank
"listpack"skiplist + hashtable 编码
元素多或值大时,同时使用两种数据结构:
- skiplist:支持范围查询和排序
- hashtable:支持 O(1) 的成员查找和分数获取
# 转换阈值
CONFIG SET zset-max-listpack-entries 128
CONFIG SET zset-max-listpack-value 64OBJECT 命令
# 查看编码
OBJECT ENCODING key
# 查看引用计数
OBJECT REFCOUNT key
# 查看空闲时间(秒)
OBJECT IDLETIME key
# 查看 LFU 访问频率
OBJECT FREQ key
# 查看帮助
OBJECT HELP编码转换总结表
| 数据类型 | 紧凑编码 | 转换条件 | 标准编码 |
|---|---|---|---|
| String | int | 非整数或 APPEND | raw |
| String | embstr | 长度 >44B 或修改 | raw |
| List | listpack | 超过 size 限制 | quicklist |
| Hash | listpack | entries >128 或 value >64B | hashtable |
| Set | intset | entries >512 或含非整数 | hashtable |
| Set | listpack (7.2+) | entries >128 或 value >64B | hashtable |
| ZSet | listpack | entries >128 或 value >64B | skiplist+hashtable |
内存优化实践
1. 利用整数编码
# 用整数 ID 做 Set 成员,使用 intset 节省内存
SADD online_users 1001 1002 1003 1004
# 用整数做 String 值,使用 int 编码
SET user:1001:age 252. 控制值大小
# Hash 字段/值控制在 64 字节内,保持 listpack 编码
HSET user:1001 name "Alice" age "25" city "Shanghai"
# 避免单个字段过大导致整个 Hash 转为 hashtable3. 合理拆分大对象
# 不推荐:一个超大 Hash
HSET bigdata field1 "value1" ... field10000 "value10000"
# 推荐:拆分为多个小 Hash(hash tag 分片)
HSET bigdata:0 field1 "value1" ... field100 "value100"
HSET bigdata:1 field101 "value101" ... field200 "value200"4. 使用 MEMORY USAGE
# 查看单个 key 的内存使用
MEMORY USAGE user:1001
# 带采样数的精确计算
MEMORY USAGE user:1001 SAMPLES 5总结
- Redis 的每种数据类型都有多种底层编码,在内存和性能之间权衡
- 紧凑编码(int/embstr/listpack/intset)内存效率高但操作复杂度较高
- 编码转换通常是单向的——从紧凑到标准,不会自动退回
- 合理设置阈值参数,在小数据量时充分利用紧凑编码
- 使用
OBJECT ENCODING和MEMORY USAGE监控和优化内存使用
贡献者
更新日志
2026/3/14 13:09
查看所有更新日志
9f6c2-feat: organize wiki content and refresh site setup于