整合集合(intset)是集合(set)的的底层实现之一,当一个集合只包含整数值元素时,并且这个集合元素数量不多时,Redis 就会使用整数集合作为集合键的底层实现。
整数集合的结构:
typedef struct intset {
uint32_t encoding; // 编码方式
uint32_t length; // 集合中元素的数量
int8_t contents[]; // 保存元素的数组
}
contents数组中的元素是排好序的(升序),不包含重复的元素
encoding的值决定了数组元素中每一项的大小限制:
INTSET_ENC_INT16 —— 2字节
INTSET_ENC_INT32 —— 4字节
INTSET_ENC_INT64 —— 8字节
(不同的字节大小可以存多大的数,心里应该是清楚的吧,嗯哼)
升级
如果即将新增的元素的大小超出了encoding属性对应编码的最大长度,那么contents数组就会先进行升级,再添加新元素。
所谓的升级,就是对数组中的每一项进行内存空间的移动和扩展。
举个例子:
我现在有如下的集合:
{1,2,3}
它们在内存中的排列(位):
[[0,15],[16,31],[32,47]]
现在我将一个65535添加入集合中,很明显,大于了当前编码类型的最大长度,所以需要扩容、升级。四个元素,总共需要,4 * 32 = 128 位的空间:
[[0,15],[16,31],[32,47],[48,127]]
给新增的元素预留出空间:
[[0,15],[16,31],[32,47],[48,63],[64,95],[96,127]] // [96,127]是给新增元素预留的空间
1,2,3,null,null,null
接下来,需要做的就是移动元素了:
[[0,15],[16,31],[32,47],[48,63],[64,95],[96,127]] // [96,127]是给新增元素预留的空间
1,2,3,null,3,null
“合并空间”并移动:
[[0,15],[16,31],[32,63],[64,95],[96,127]] // [96,127]是给新增元素预留的空间
1,2,3,null
“合并空间”:
[[0,31],[32,63],[64,95],[96,127]] // [96,127]是给新增元素预留的空间
1,2,3,null
添加新增元素:
[[0,31],[32,63],[64,95],[96,127]] // [96,127]是给新增元素预留的空间
1,2,3,65535
由于每一次的新增元素都有可能会导致升级现象,所以新增元素的时间复杂度是O(N)
升级的好处
提升灵活性、节约内存
(我感觉有点牵强)
一旦对数组进行升级操作后,编码就会一直保持,直到下一次升级。即,没有“降级”的操作。
2024.10.21
writeBy kaiven