1. 程式人生 > 其它 >Redis原理再學習05:資料結構-整數集合intset

Redis原理再學習05:資料結構-整數集合intset

intset介紹

intset 整數集合,當一個集合只有整數元素,且元素數量不多時,Redis 就會用整數集合作為集合鍵的底層實現。

redis> SADD numbers 1 3 5 7 9

(integer 5)

redis> OBJECT ENCODING numbers

(inset)

為什麼要用 intset?

集合鍵的另外一種實現是值為空的散列表(hash table),當元素比較少時,用hash table 存就比較浪費記憶體,而用

intset 就比較節約記憶體。

整數集合實現

intset 結構定義:

// https://github.com/redis/redis/blob/3.0/src/intset.h#L35
typedef struct intset {
    uint32_t encoding; // 編碼格式, 這個格式有3種,見下面
    uint32_t length; // 集合元素的數量
    int8_t contents[]; // 儲存元素的陣列
} intset;

// https://github.com/redis/redis/blob/3.0/src/intset.c#L40
/* Note that these encodings are ordered, so:
 * INTSET_ENC_INT16 < INTSET_ENC_INT32 < INTSET_ENC_INT64. */
#define INTSET_ENC_INT16 (sizeof(int16_t)) // 16位2個位元組
#define INTSET_ENC_INT32 (sizeof(int32_t)) // 32位4個位元組
#define INTSET_ENC_INT64 (sizeof(int64_t)) // 64位8個位元組

intset 整數集合結構示意圖:

雖然,intset 整數集合裡欄位 contents 宣告是 int8_t 資料型別,但是 contents 儲存的資料型別是根據 encoding 屬性值來確定的。

  • encoding 值為 INTSET_ENC_INT16 時,contents 就是一個 int16_t 型別的陣列,數組裡每一項都是 int16_t 型別的整數值。最小值為 -32768,最大值為 32767。
  • 同理,encoding 值為 INTSET_ENC_INT32 時,contents 就是一個 int32_t 型別的陣列,數組裡每一項都是 int32_t 型別的整數值。最小值為 -2,147,483,648,最大值為 2,147,483,647。
  • 同理,encoding 值為 INTSET_ENC_INT64 時,contents 就是一個 int64_t 型別的陣列,數組裡每一項都是 int64_t 型別的整數值。最小值為 -9,223,372,036,854,775,808,最大值為 9,223,372,036,854,775,807。

encoding 的值為什麼有 3 種呢?

為了節省記憶體。redis 可以根據儲存的元素數值大小,選擇合適的型別來儲存。

比如新增新元素時,元素整數值超過了當前編碼格式能表示的範圍,就升級資料型別。

整數集合操作的一些 API

整數集合操作一些 API:

// https://github.com/redis/redis/blob/3.0/src/intset.h#L41
intset *intsetNew(void);  // 建立空集合
intset *intsetAdd(intset *is, int64_t value, uint8_t *success); // 將 value 新增到 is 集合中
intset *intsetRemove(intset *is, int64_t value, int *success); // 將 value 從 is 集合中移除
uint8_t intsetFind(intset *is, int64_t value); // 在結合 is 中搜索 value 元素,成功返回1,失敗返回0
int64_t intsetRandom(intset *is); // 隨機返回一個元素
uint8_t intsetGet(intset *is, uint32_t pos, int64_t *value); // 獲取下標為pos的元素值並保持在value中
uint32_t intsetLen(intset *is); // 計算集合中元素個數
size_t intsetBlobLen(intset *is); // 計算集合中元素所佔位元組總數

獲取元素編碼格式函式:

_intsetValueEncoding

// https://github.com/redis/redis/blob/3.0/src/intset.c#L45
/* Return the required encoding for the provided value. */
static uint8_t _intsetValueEncoding(int64_t v) {
    if (v < INT32_MIN || v > INT32_MAX)
        return INTSET_ENC_INT64;
    else if (v < INT16_MIN || v > INT16_MAX)
        return INTSET_ENC_INT32;
    else
        return INTSET_ENC_INT16;
}

參考