Set 编码是什么
Redis 的集合(Set)在底层有三种内部编码:intset(整数数组)、listpack(紧凑列表,7.2 起用于小集合)和 hashtable(哈希表)。调用方选不出编码,Redis 根据元素是否为整数、元素个数是否越过阈值自动决定。编码不改变命令语义,但显著改变内存占用,几十万个整数 ID 的集合从 intset 变成 hashtable 后内存涨好几倍是常态,所以做容量规划与内存排查时必须先看编码。
基本信息
| 适用版本 | Redis 3.2+(intset / hashtable);listpack 编码 7.2+ |
|---|---|
| 观察命令 | OBJECT ENCODING key;MEMORY USAGE key 看实际字节 |
| intset 阈值 | set-max-intset-entries,默认 512 |
| listpack 阈值 | set-max-listpack-entries,默认 128 |
| 触发条件 | 集合内全部是十进制整数且数量不超阈值才可能用 intset |
典型使用场景
- 内存排查:某个 Set 占用异常高时,先用 OBJECT ENCODING 确认是否已从 intset 掉进 hashtable
- 容量规划:把几百万用户 ID 放进集合前,先估算编码与元素数量带来的内存量级
- 版本升级评估:7.2 引入 listpack 小集合编码,升级后可用同一批键对比升级前后的编码与内存
- 调参验证:调整 set-max-intset-entries 或 set-max-listpack-entries 后,用重建的键验证是否按新阈值生效
- 数据结构选型:需要去重且都是整数时,Set 比 Hash 更省内存,编码是关键依据
观察编码变化
观察编码变化
# 纯整数的集合走 intset,内存最省
SADD nums 1 2 3
OBJECT ENCODING nums # -> intset
# 一旦混入非整数字符串,整个集合立刻转 hashtable,且不可逆
SADD nums abc
OBJECT ENCODING nums # -> hashtable
# 字符串元素的小集合(7.2+)走 listpack
SADD str x y z
OBJECT ENCODING str # -> listpack(7.2+)或 hashtable
CONFIG GET set-max-intset-entries
MEMORY USAGE nums # 想知道真实字节数看这个,别只看编码三种编码对照
| 编码 | 触发条件 | 内存特点 | 常见命令代价 |
|---|---|---|---|
| intset | 全整数且数量不超 512 | 最省,有序整数数组 | SISMEMBER 二分查找 O(log N) |
| listpack | 7.2+ 小集合(数量不超 128 且元素小) | 紧凑连续,省指针开销 | 线性扫描,集合小所以可接受 |
| hashtable | 含非整数或数量超阈值 | 每元素要哈希桶与指针,开销大 | SISMEMBER 平均 O(1),但内存高 |
编码转换是单向的:intset 转成 hashtable 后,即使把引起转换的字符串成员删掉也不会退回 intset,想恢复只能把成员读到客户端、删键、再 SADD 重建。
修改 set-max-intset-entries 这类阈值不会让已存在的键自动重新编码;只有新建键(或重建已有键)时才会按当前阈值判断。