Redis資料結構—整數集合與壓縮列表

白澤來了發表於2021-05-16

Redis資料結構—整數集合與壓縮列表

大家好,我是白澤。今天我們將學習Redis的整數集合與壓縮列表這兩個資料結構,且在本文中我將盡量只描述這兩種結構中重要的部分,而非面面俱到,因為我學Redis資料結構的初衷是為了我能更好理解後面要講到的Redis物件,而非真的去研究Redis深層的實現,不會過分深入,夠用就好

Redis物件的實現在底層用到了我們目前講解到的這些資料結構:簡單動態字串SDS、雙端連結串列、字典、以及今天要講解的整數集合與壓縮列表,因此從底層資料結構開始能為我們理解Redis打下堅實的基礎,成為面試殺手

整數集合的實現

整數集合是Redis用於儲存整數值的集合抽象資料結構,它可以儲存int16_t、int32_t、int64_t的整數值,且保證集合中不出現重複元素

contents[]陣列的型別這裡看上去是int8_t,但事實上contents[]的資料型別依靠encoding的屬性決定(encoding可以選擇為INTSET_ENC_INT16、INTSET_ENC_INT32、INTSET_ENC_INT64表示contents[]中元素的資料型別)

typedef struct intset {
	uint32_t encoding;	//編碼方式
	uint32_t length;	//集合包含的元素的個數
	int8_t contents[];	//儲存元素的陣列
} inset;

下面給出的圖片是一個包含了五個int16_t型別整數值的整數集合,int16_t表示每個整數是由16位共2個位元組表示,因此此時contents[]陣列大小為:5 * 16 = 80位

整數集合的升級

當我們向一個已經存在的整數集合中新增元素時,如果加入的元素的資料型別比contents[]陣列元素的資料型別長,則整數集合需要進行升級,舉個例子,我要在上面那個大小5的16位整數集合中插入一個型別為int32_t的整數65535:

  1. 升級首先要做的是,根據新元素的長度,擴大空間,目前有5個元素,加入一個後有6個,且int16_t的元素要升級為int32_t型別(需要6 * 32 = 192位的空間),因此需要先動態分配記憶體空間如下:

  1. 移動元素的位置,將14632往後移動到新的位置,末尾留下一個位置存放32位的65535

  1. 同理移動233、18、-5、-6370,並最後將末尾的空間中存入65535

整數集合不支援降級

如果因為插入一個32位的整數使得原本16位的contents[]陣列轉變為32位,後面又刪去了這個32位的整數,這個整數集合將不會降級成16位的contents[]陣列

壓縮列表的構成

因為壓縮列表好像面試中不太出現,所以略講~

壓縮列表就是一塊連續的記憶體,一種順序型的資料結構,entryX是一個個節點,存放資料,其他屬性用於描述整個壓縮列表的整體情況(佔用記憶體、節點個數等)

zlbytes:記錄整個壓縮列表佔用的記憶體位元組數

zltail:記錄壓縮列表表尾節點距離壓縮列表的起始地址的偏移量

zllen:記錄了壓縮列表中節點數量

entryX:壓縮列表的各個節點

zlend:特殊值0xFF,用於標記壓縮列表的末端

壓縮列表節點的構成

節點就是一個個的entryX,是壓縮列表的核心,每個節點由三部分組成:

  1. previous_entry_length:記錄前一個節點的長度,因此可以從當前節點的起始地址計算出前一個節點的起始地址,從而實現壓縮列表的逆序遍歷
    • 如果前一節點的長度小於254位元組,則previous_entry_length屬性的長度為1位元組:用於儲存前一節點的長度
    • 如果前一節點的長度大於等於254位元組,則previous_entry_length屬性的長度為5位元組:用於儲存前一節點的長度
  2. encoding:記錄的content屬性所儲存資料的型別以及長度
  3. content:負責儲存節點的值(一個位元組陣列或整數)

連鎖更新

存在這樣一種情況,有一個壓縮列表,內部節點的長度都是253位元組的,突然要插入一個長度超過254的表頭節點entry0到entry1前面,此時由於entry1中的previous_entry_length需要儲存前一個節點的長度,而entry0長度超過254位元組,因此entry1原本的previous_entry_length屬性需要從1位元組擴充套件為5位元組,繼而導致entry1節點的長度由253位元組變為253+4 = 257位元組,而entry2的previous_entry_length也會因為entry1節點整體長度大於254位元組而擴充套件為5位元組,以此類推,整個節點列表後面的節點產生類似連鎖反應,都需要擴充套件

增加、刪除節點都有可能引發連鎖更新

小結

到目前為止,白澤已經帶大家初步學習了Redis底層所有的資料結構:簡單動態字串SDS、雙端連結串列、字典、跳躍表、整數集合、壓縮列表,下一篇部落格將帶大家學習Redis的物件(Redis的物件底層就是這些資料結構實現的),再後面就正式講解Redis資料庫的知識點了,敬請期待~

相關文章