Redis——intset

753 阅读2分钟

前言

Set 结构的底层是采用 hash 表实现的,但是在所有元素都是整数的时候,redis 会使用 intset 作为底层结构,这是一种内存更加紧凑的数据类型,数字会进行排序,使用二分法查询数据。

一个 Set 要用 intset 作为底层存储的话,需要满足两个条件:

  • 元素都是整数类型;
  • 另一个条件是这个 Set 里面的元素个数,要少于 set-max-intset-entries 配置指定的这个值,这个值默认是 512。

一旦这个 Set 集合不满足这两个条件,就会切换成 hash 表作为底层存储。Redis 之所以使用 intset 结构来进行优化,主要是为了减少内存碎片,可以充分使用 CPU 高速缓存

结构

typedef struct intset {
    uint32_t encoding;
    uint32_t length;
    int8_t contents[];
} intset;
  1. encoding,数据编码
  2. length,元素数量
  3. contents,存放数据的数组

encoding 是表示 contents 数组中存储的都是什么类型的数据,它有以下几种类型:

  • 2: 表示所有数字都在 [ -2^16,2^16 - 1 ] 范围内,每个数字都使用 2 个字节表示。
  • 4: 表示有数字在 [ -2^32, 2^32 - 1 ] 范围内, 每个数字都使用 4 个字节表示。
  • 8: 表示有数字在 [ -2^64, 2^64 - 1 ] 范围内, 每个数字都使用 8 个字节表示。

image.png

添加

添加元素有以下几个步骤:

  1. 首先会判断该数字是否符合 encode

    1. 如果不符合,那么就需要进行升级
    2. 如果符合,那么会通过二分法先找到需要插入的位置
  2. 然后会重新分配内存空间,将内存容量增大一个数字的空间。

  3. 将需要插入位置后面的数字都后移

  4. 最后将新数字插入到位置中

image.png

升级

Intset 中不同的 encode,元素所占用的字节数是不一样的。当 encode 为 2 时,所有的元素都占用 2 个字节。如果添加一个数字 32768,因为 32768 使用 2 个字节无法存下,所以 intset 需要进行升级。

升级步骤:

  1. 计算新的 encode,新的 encode 为 4.
  2. 计算并分配需要使用的内存,总共需要使用的内存空间 = 4 + 4 + (length + 1)* 4
  3. 将旧 intset 中的数字复制到新的 intset 中,

image.png