Redis原理再學習05:資料結構-整數集合intset
阿新 • • 發佈:2022-03-11
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); // 計算集合中元素所佔位元組總數
獲取元素編碼格式函式:
// 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;
}