圖解Redis之資料結構篇——整數集合

道友留步`發表於2019-07-30

前言

    整數集合(intset)並不是一個基礎的資料結構,而是Redis自己設計的一種儲存結構,是集合鍵的底層實現之一,當一個集合只包含整數值元素,並且這個集合的元素數量不多時, Redis i就會使用整數集合作為集合鍵的底層實現。

一、整數集合實現

    整數集合(intset)是Redis用於儲存整數值的集合抽象資料結構,它可以儲存型別為int16_t、int32_t或者int64_t的整數值,並且保證集合中不會出現重複元素。

//每個intset結構表示一個整數集合
typedef struct intset{
    //編碼方式
    uint32_t encoding;
    //集合中包含的元素數量
    uint32_t length;
    //儲存元素的陣列
    int8_t contents[];
} intset;
  • contents陣列是整數集合的底層實現,整數集合的每個元素都是 contents陣列的個陣列項(item),各個項在陣列中按值的大小從小到大有序地排列,並且陣列中不包含任何重複項。
  • length屬性記錄了陣列的長度。
  • intset結構將contents屬性宣告為int8_t型別的陣列,但實際上 contents陣列並不儲存任何int8t型別的值, contents陣列的真正型別取決於encoding屬性的值。encoding屬性的值為INTSET_ENC_INT16則陣列就是uint16_t型別,陣列中的每一個元素都是int16_t型別的整數值(-32768——32767),encoding屬性的值為INTSET_ENC_INT32則陣列就是uint32_t型別,陣列中的每一個元素都是int16_t型別的整數值(-2147483648——2147483647)。

int16_t

    如上圖,為一int16_t型別的整數集合,我們可以看到陣列中儲存了5個int16_t型別的整數,它們按照從小到大的順序依次排列。這個時候我們思考一個問題。如果這個時候存入一個int32_t型別的整數會怎麼樣?記憶體溢位?這個時候就要提到整數集合的升級。

二、整數集合的升級

2.1 整數集合升級過程

    正如上面所提到的問題,每當我們要將一個新元素新增到整數集合裡面,並且新元素的型別比整數集合現有所有元素的型別都要長時,整數集合需要先進行升級,然後才能將新元素新增到整數集合裡面。升級整數集合並新增新元素主要分三步來進行。

  1. 根據新元素的型別,擴充套件整數集合底層陣列的空間大小,併為新元素分配空間。
  2. 將底層陣列現有的所有元素都轉換成與新元素相同的型別,並將型別轉換後的元素放置到正確的位上,而且在放置元素的過程中,需要繼續維持底層陣列的有序性質不變。
  3. 將新元素新增到底層陣列裡面。

圖解Redis之資料結構篇——整數集合

2.2 整數集合升級的優點

  1. 提升靈活性

    因為C語言是靜態型別語言,為了避免型別錯誤,我們通常不會將兩種不同型別的值放在同一個資料結構裡面。

    例如,我們一般只使用int16_t型別的陣列來儲存int16_t型別的值,只使用int32_t型別的陣列來儲存int32_t型別的值,諸如此類。但是,因為整數集合可以通過自動升級底層陣列來適應新元素,所以我們可以隨意地將int16_t、int32_t或者int64_t型別的整數新增到集合中,而不必擔心出現型別錯誤,這種做法非常靈活。

  1. 節約記憶體

    要讓一個陣列可以同時儲存int16_t、int32_t、int64_t三種型別的值,最簡單的做法就是直接使用int64t型別的陣列作為整數集合的底層實現。不過這樣一來,即使新增到整數集合裡面的都是int16_t型別或者int32_t型別的值,陣列都需要使用int64_t型別的空間去儲存它們,從而出現浪費記憶體的情況。

而整數集合現在的做法既可以讓集合能同時儲存三種不同型別的值,又可以確保升級操作只會在有需要的時候進行,這可以儘量節省記憶體。如果我們一直只向整數集合新增int16_t型別的值,那麼整數集合的底層實現就會一直是int16_t型別的陣列,只有在我們要將int32_t型別或者int64_t型別的值新增到集合時,程式才會對陣列進行升級。

2.3 降級

    整數集合不支援降級操作,一旦對陣列進行了升級,編碼就會一直保持升級後的狀態。也就是說一旦我們向一個int16_t的整數集合內新增了一個int32_t的元素後,整數集合將升級到int32_t型別。即使後續的操作中我們刪除了這個元素,整數集合還是會保持int32_t型別的狀態。

三、整數集合常用操作時間複雜度

操作 時間複雜度
建立一個新的整數集合 O(1)
新增指定元素到集合 O(N)
移除指定元素 O(N)
判斷指定元素是否在集合中 O(logN)
隨機返回一個元素 O(1)
取出在指定索引上的元素 O(1)
返回集合包含的元素個數 O(1)
返回集合佔用的記憶體位元組數 O(1)

本文重點

  • 整數集合是Redis自己設計的一種儲存結構,集合鍵的底層實現之一。
  • 整數集合的底層實現為陣列,這個陣列以有序、無重複的方式儲存集合元素,在有需要時,程式會根據新新增元素的型別,改變這個陣列的型別。
  • 升級操作為整數集合帶來了操作上的靈活性,並且儘可能地節約了記憶體。
  • 整數集合只支援升級操作,不支援降級操作。

參考

《Redis設計與實現》

《Redis開發與運維》

《Redis官方文件》

-----END-----

相關文章