IntSet通过encoding字段动态选择INTSET_ENC_INT16/INT32/INT64编码,插入超范围值时自动升级且不降级:先按新编码扩容contents数组,再倒序迁移原元素并插入新值,全程保持有序和内存紧凑。

Redis 的整数集合(IntSet)通过编码(encoding)字段动态适配不同大小的整数类型,并在插入超出当前容量的值时自动执行升级(upgrade),整个过程无需用户干预,也不降级。
IntSet 的三种编码类型
IntSet 底层只用一个柔性数组 contents[] 存储数据,但实际每个元素占多少字节,由 encoding 决定:
-
INTSET_ENC_INT16:每个元素占 2 字节,支持范围
-32768 ~ 32767 -
INTSET_ENC_INT32:每个元素占 4 字节,支持范围
-2147483648 ~ 2147483647 -
INTSET_ENC_INT64:每个元素占 8 字节,支持范围
-9223372036854775808 ~ 9223372036854775807
注意:contents 声明为 int8_t[] 是为了内存对齐和灵活寻址,真正读写时会按 encoding 类型做指针偏移和字节拷贝。
升级触发条件
当调用 SADD 向 IntSet 插入新整数时,Redis 会先判断该值所需最小编码(通过 _intsetValueEncoding(value) 计算),再与当前 is->encoding 比较:
- 若新值编码 ≤ 当前 encoding:直接查找插入位置(保持有序),不升级
- 若新值编码 > 当前 encoding:必须升级,例如从 int16 升到 int32
升级只发生在“写入更大类型值”时,不会因删除或后续插入小值而回退——没有降级机制。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
升级三步操作
升级不是简单扩容,而是重构整个存储结构:
- 按新 encoding 和新长度(
length + 1)重新分配contents内存空间 - 倒序遍历原数组,把每个旧元素按新类型重新写入对应位置(保证升序不变)
- 将新值插入末尾(或指定有序位置),更新
encoding和length
例如:已有 {1, 2, 3}(int16 编码),插入 65535(超出 int16 范围)→ 触发升级为 int32 → 原三个数被复制到新数组的索引 0、1、2 处,65535 放入索引 3。
为什么设计成只升级不降级
降级虽可节省内存,但代价高:
- 需全量扫描确认所有元素是否仍能被更小类型表示
- 频繁升降级会抵消 IntSet 的紧凑优势
- 实际业务中,集合数值范围通常趋于扩大而非收缩
所以 Redis 选择“一次升级,长期有效”,兼顾性能与实现简洁性。

















