Redis集合对象底层自动在intset和hashtable间转换:纯整数且数量≤set-max-intset-entries(默认512)时用intset,否则升为hashtable且不可逆;intset内部支持16/32/64位整数类型升级,不触发编码变更。

Redis 集合对象的底层转换不是手动触发的,而是由 Redis 自动根据数据特征和配置阈值实时判断、无缝升级的。核心逻辑围绕两个结构的适用边界展开:intset(整数集合)用于轻量、纯整数场景;hashtable(哈希表)兜底通用、大规模或混合类型场景。
intset 和 hashtable 的切换条件很明确
-
集合必须同时满足以下两点,才用 intset 编码:
- 所有元素都是整数值(例如
1,-100,32767),不能是"1"字符串或"abc"; - 元素总数量 ≤
set-max-intset-entries配置值(默认是 512)。
- 所有元素都是整数值(例如
只要违反其中任一条件,Redis 就立刻将底层从 intset 升级为 hashtable,且该过程不可逆。
例如:
127.0.0.1:6379> SADD myset 1 2 3 (integer) 3 127.0.0.1:6379> OBJECT ENCODING myset "intset"
此时是 intset;但只要执行:
127.0.0.1:6379> SADD myset "hello" (integer) 1 127.0.0.1:6379> OBJECT ENCODING myset "hashtable"
插入一个字符串,编码瞬间变成 hashtable —— 不需要重启、不阻塞命令、用户无感知。
intset 内部还有“类型升级”,和哈希表无关但影响内存布局
intset 本身支持三种整数编码:INTSET_ENC_INT16(每个元素占 2 字节)、INTSET_ENC_INT32(4 字节)、INTSET_ENC_INT64(8 字节)。
当插入一个超出当前编码范围的整数时,intset 会一次性扩容并重写整个数组,把所有老元素转成更大类型。
比如初始存 1, 2, 3 → 用 INT16;
再插入 65536(>65535)→ 整个 intset 升级为 INT32,所有元素重新存储。
这个过程仍保持 intset 编码,不触发到 hashtable 的转换。
hashtable 是真正的“万能模式”
一旦进入 hashtable 编码:
- 所有元素都作为 key 存入字典,value 固定为 NULL;
- 支持任意字符串(包括数字字符串
"123"、空格、二进制数据); - 查找、添加、删除平均时间复杂度为 O(1);
- 内存开销明显高于 intset(指针、哈希桶、空闲槽位等);
- 即使后续删光非整数、只剩几个小整数,也不会降级回 intset。
你可以通过配置调整 intset 的容量上限:
CONFIG SET set-max-intset-entries 1024
但注意:已升级为 hashtable 的集合不会因此回退。
实际使用中要注意的细节
-
SADD是唯一可能触发编码变更的写命令(其他如SREM、SISMEMBER都只读或不改变结构); -
SMEMBERS返回顺序在 intset 下是升序(因有序数组),在 hashtable 下是哈希遍历顺序(看似随机,实际稳定); - 监控编码变化可用
OBJECT ENCODING,排查内存异常时很有用; - 如果业务确定只存整数且总量可控,可适当调高
set-max-intset-entries,但超过几千后 intset 的 O(log n) 查找优势减弱,hashtable 的 O(1) 更稳。
不复杂但容易忽略。











