redis數據結構介紹三-第三部分 整數集合

本文使用「署名 4.0 國際 (CC BY 4.0)」許可協議,歡迎轉載、或從新修改使用,但須要註明來源。 署名 4.0 國際 (CC BY 4.0)redis

本文做者: Nicksxs數組

建立時間: 2020年01月10日數據結構

本文連接: redis數據結構介紹二-第二部分 跳錶
redis中對於 set 其實有兩種處理,對於元素均爲整型,而且元素數目較少時,使用 intset 做爲底層數據結構,不然使用 dict 做爲底層數據結構,先看一下代碼先flex

typedef struct intset {
    // 編碼方式
    uint32_t encoding;
    // 集合包含的元素數量
    uint32_t length;
    // 保存元素的數組
    int8_t contents[];
} intset;

/* Note that these encodings are ordered, so:
 * INTSET_ENC_INT16 < INTSET_ENC_INT32 < INTSET_ENC_INT64. */
#define INTSET_ENC_INT16 (sizeof(int16_t))
#define INTSET_ENC_INT32 (sizeof(int32_t))
#define INTSET_ENC_INT64 (sizeof(int64_t))

一眼看,爲啥整型還須要編碼,而後 int8_t 怎麼能存下大整形呢,帶着這些疑問,咱們一步步分析下去,這裏的編碼其實指的是這個整型集合裏存的到底是多大的整型,16 位,仍是 32 位,仍是 64 位,結構體下面的宏定義就是表示了 encoding 的可能取值,INTSET_ENC_INT16 表示每一個元素用2個字節存儲,INTSET_ENC_INT32 表示每一個元素用4個字節存儲,INTSET_ENC_INT64 表示每一個元素用8個字節存儲。所以,intset中存儲的整數最多隻能佔用64bit。length 就是正常的表示集合中元素的數量。最奇怪的應該就是這個 contents 了,是個 int8_t 的數組,那放毛線數據啊,最小的都有 16 位,這裏我在看代碼和《redis 設計與實現》的時候也有點懵逼,後來查了下發現這是個比較取巧的用法,這裏我用本身的理解表述一下,先看看 8,16,32,64 的關係,一眼看就知道都是 2 的 N 次,而且呈兩倍關係,並且 8 位恰好一個字節,因此呢其實這裏的contents 不是個常規意義上的 int8_t 類型的數組,而是個柔性數組。看下 wiki 的定義ui

Flexible array members 1 were introduced in the C99 standard of the C programming language) (in particular, in section §6.7.2.1, item 16, page 103). 2 It is a member of a struct, which is an array without a given dimension. It must be the last member of such a struct and it must be accompanied by at least one other member, as in the following example:
struct vectord {
    size_t len;
    double arr[]; // the flexible array member must be last
};

在初始化這個 intset 的時候,這個contents數組是不佔用空間的,後面的反正用到了申請,那麼這裏就有一個問題,給出了三種可能的 encoding 值,他們能隨便換嗎,顯然不行,首先在 intset 中數據的存放是有序的,這個有部分緣由是方便二分查找,而後存放數據其實隨着數據的大小不一樣會有一個升級的過程,看下圖

新建立的intset只有一個header,總共8個字節。其中encoding = 2, length = 0, 類型都是uint32_t,各佔 4 字節,添加15, 5兩個元素以後,由於它們是比較小的整數,都能使用2個字節表示,因此encoding不變,值仍是2,也就是默認的 INTSET_ENC_INT16,當添加32768的時候,它再也不能用2個字節來表示了(2個字節能表達的數據範圍是-215~215-1,而32768等於215,超出範圍了),所以encoding必須升級到INTSET_ENC_INT32(值爲4),即用4個字節表示一個元素。在添加每一個元素的過程當中,intset始終保持從小到大有序。與ziplist相似,intset也是按小端(little endian)模式存儲的(參見維基百科詞條Endianness)。好比,在上圖中intset添加完全部數據以後,表示encoding字段的4個字節應該解釋成0x00000004,而第4個數據應該解釋成0x00008000 = 32768編碼

相關文章
相關標籤/搜索