天天看點

redis 系列8 資料結構之整數集合

原文: redis 系列8 資料結構之整數集合

一.概述

  整數集合(intset)是集合鍵的底層實作之一, 當一個集合隻包含整數值元素,并且這個集合元素數量不多時, Redis就會使用整數集合作為集合鍵的底層實作。下面建立一個隻包含5個元素的集合鍵,并且集合中所有元素都是整數值,那麼這個集合鍵的底層實作就會是整數集合。 接着添加非整數值,集合鍵的底層實作就會是hashtable。

127.0.0.1:6379> sadd numbers 1 3 5 7 9 
(integer) 5
127.0.0.1:6379> object encoding numbers
"intset"
127.0.0.1:6379> sadd numbers 'one'
(integer) 1
127.0.0.1:6379> object encoding numbers
"hashtable"      

二. 整數集合實作

  整數集合是Redis用于儲存整數值的集合抽象資料結構,它可以儲存類型為int16_t, int32_t, int64_t的整數值,并且保證集合中不會出現重複元素。資料集合定義如下:

// 每個intset.h/intset結構表示一個整數集合
           typedef struct intset
        {
            //編碼方式
            uint32_t encoding;
            //集合包含的元素數量
             uint32_t  length;
            //儲存元素的數組
             int8_t contents[];
        }intset;      

  (1) contents數組是整數集合的底層實作,整數集合的每個元素都是contents數組的一個數組項(item),各個項在數組中按值從小到大有序排列,并且數組中不包含重複項。如下面腳本:

127.0.0.1:6379> sadd record 5 3 4 5 6 0
(integer) 5
127.0.0.1:6379> smembers record
1) "0"
2) "3"
3) "4"
4) "5"
5) "6"      

  (2) length屬性記錄了整數集合包含的元素數量,也即是contents數組的長度。雖然contents屬性聲明為int8_t類型的數組,但實作上contents數組并不儲存任何int8_t類型的值,contents數組的真正類型取決于encoding屬性的值。

  a. 如果encoding 屬性的值為intset_enc_int16,那麼contents就是一個int16_t類型的數組,數組裡的每個項都是一個int16_t類型的整數值(範圍在 -32768 ~ 32767)。如下圖encoding屬性的值有5個整數型,根據這些整數值得出encoding為int16_t類型。

redis 系列8 資料結構之整數集合

  b. 如果encoding屬性的值為intset_enc_int32, 那麼數組裡每個項就是一個int32_t類型的整數值(範圍在 -2147483648 ~ 2147483647)。還有encoding屬性的值為intset_enc_int64類型的,數組裡每個項取取值範圍更大。

  需要注意的是:假設contents數組儲存的值為2147483647, 1,2,3 四個整數值。 但隻有第一個整數值需要用int32_t類型來儲存,而其它三個值可以用int16_t類型來儲存。不過根據整數集合的更新規則,當一個底層的int16_t數組的整數集合添加一個int64_t類型的整數值時,整數集合中所有元素都會被轉換成int64_t類型。 是以contents數組儲存的整數值都是int64_t類型的。

三. 更新

   當我們要将一個新元素添加到整數集合裡面,并且新元素的類型比整數集合現有所有元素的類型都要長時,整數集合需要先進行更新,然後才能将新元素添加到整數集合中。假設:集合中包含三個int16_t類型的元素,值分别是1,2,3 。因為每個元素都占用16位空間,是以整數集合底層數組的大小 為3 * 16 =48位。現将int32_t的數值65535添加進去,這裡程式需要對整數集合進行更新。

  更新整數集合并添加新元素共分三步進行:

    (1) 根據新元素的類型,擴充整數集合底層數組的空間大小 ,并為新元素配置設定空間。配置設定空間後,現在整數集合4個元素的底層數組大小為4 *32 =128位, 此時前三位還是48位空間,如下圖所示:

redis 系列8 資料結構之整數集合

    (2) 将底層數組現有的所有元素都轉換成與新元素相同的類型(需要從int16_t 轉成int32_t所需的空間) ,轉換後元素位置有序不變,如下圖所示:

redis 系列8 資料結構之整數集合

    (3) 将新元素添加到底層數組裡面,如下圖所示:

redis 系列8 資料結構之整數集合

四. 更新的好處

  4.1 提升靈活性

    為了避免類型錯誤,通常不會将兩種不同類型的值放在同一個資料結構裡面,通過更新處理可以随意地将int16_t, int32_t, , int64_t 類型的整數添加到集合中,而不必擔心出現類型錯誤。

       4.2 節約記憶體

    要讓一個數組可以同時儲存int16_t,int32_t, , int64_t三種類型的值,最簡單的做法就是直接使用int64_t類型的數組作為整數集合的底層實作,不過這樣浪費記憶體空間。

五.   降級

  整數集合不支援降級操作,一旦對數組進行了更新,編碼就會一直保持更新後的狀态。即使集合裡隻有一個需要使用int64_t類型的元素被删除了,整數集合的編碼仍然會維持intset_enc_int64, 底層數組也仍然會是int64_t類型,如下圖所示:

redis 系列8 資料結構之整數集合

六. 整數集合API

函數 作用
intsetNew 建立一個新的壓縮清單
intsetAdd 将給定元素添加到整數集合裡面
intsetRemove 從整數集合中移除給定元素
intsetFind 檢查給定值是否存在于集合
intsetRandom 從整數集合中随機傳回一個元素
intsetGet 取出底層數組在給定索引上的元素
intsetLen 傳回整數集合包含的元素個數
intsetBloblen 傳回整數集合占用的記憶體位元組數

  

繼續閱讀