1_5_Redis设计与实现读书记-整数集合

260 阅读3分钟

整数集合

整数集合(intset)是集合键的底层实现之一。当集合只包含整数值元素,并且元素数量不多,Redis就会使用整数集合作为集合键的底层实现。

1. 整数集合的实现

Redis用于保存整数值的集合抽象数据结构。保存的类型为:int16_t、int32_t或者int64_t 的整数值,并且保存集合中不出现重复元素。

intset集合结构:

typedef struct intset{
    // 编码方式
    uint32_t encoding;
    // 集合包含的元素数量
    uint32_t length;
    // 保存元素的数组
    int8_t contents[];
}intset;

contents 数组是整数集合的底层实现,每个元素都是contents数组的一个数组项(item),数组中按值的大小从小到大有序的排列,且数组中不包含重复项。

**注:**数组虽然为int8_t类型,但是保存数据的类型,由encoding属性值决定。

length 记录了集合中包含的元素数量,是contents数组的长度。

encoding 属性:

​ (1) INTSET_ENC_INT16类型,contents就是一个int16_t类型的数组,每项都是一个int16_t类型的整数值(-32768~32767)。

​ (2) INTSET_ENC_INT32类型,contents就是一个int32_t类型的数组,每项都是一个int32_t类型的整数值(-2147483648~2147483647)。

​ (3) INTSET_ENC_INT64类型,contents就是一个int64_t类型的数组,每项都是一个int64_t类型的整数值(-9223372036854775808~9223372036854775807)。

如上图:

​ encoding 属性值为INTSET_ENC_INT16,则保存的是int16_t类型的数值。contents数组按从小到大的顺序保存四个元素,length属性为4。contents数组的大小为 sizeof(int16_t) * 4 = 16 * 4 = 64位(bit)。

contents数组保存数据的规则:当底层为int16_t数组的整数集合添加一个int64_t类型的整数值时,那么集合中已有的元素都会被转换成int64_t类型。

2. 升级

当新添加的元素比整数集合现有的所有元素都要长的时候,证书集合需要先进行升级(upgrade),然后才能将新元素添加到整数集合中。

整数集合的升级步骤:

  1. 根据新元素类型,扩展整数集合底层数组的大小,为新元素分配空间。
  2. 将现有元素类型转换成新元素相同类型,并将转换后的元素放到对应位置。放置元素的过程中,维持底层数组的有序性不变。
  3. 将新添元素添加到整数集合数组中。

**注:**升级首先做的是,根据新元素类型的长度,以及集合元素的数量(包含新增),对底层数组空间重分配。

​ 因为每次的添加新元素都可能会引起升级,每次升级都需要对底层数组已有的元素进行类型转换,则新元素的添加操作时间复杂度为O(N)。


![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/12/11/16ef3a4d3944c931~tplv-t2oaga2asx-image.image)

3. 升级的好处

提升整数集合的灵活性;尽可能地节约内存。

3.1 提升灵活性

因为C语言是静态类型语言的特点,避免错误,通常不会把两种不同类型的值放在同一个数据结构里。

3.2 节约内存

若让一个数组同时保存int16_t、int32_t、int64_t三种类型的值,最简单的方法就是使用int64_t类型的数组作为整数集合数组的底层实现。同时,如果保存的是int16_t类型的元素,则数组都需要使用int64_t类型的空间保存,则会造成内存浪费。整数集合的做法满足了同时保存三种不同类型值的需求,也可以在有需要的时候进行。

4. 降级

整数集合不支持降级操作,升级过后,则一直保持在升级后的状态。

5. 整数集合 API

6. 回顾