《Redis設計與實現》第7章 壓縮列表(ziplist)

       壓縮列表(ziplist)是列表鍵和哈希鍵的底層實現之一。

       當一個列表鍵只包含少量列表項,並且每個列表項要麼就是小整數值,要麼就是長度比較短的字符串,那麼Redis就會使用壓縮列表來作爲列表鍵的底層實現。

       當一個哈希鍵只包含少量鍵值對,並且每個鍵值對的鍵和值要麼就是小整數值,要麼就是長度較短的字符串時,那麼Redis就會使用壓縮列表來做哈希鍵的底層實現。

7.1 壓縮列表的構成

       壓縮列表是Redis爲了節約內存而開發的,是由一系列特殊編碼的連續內存塊組成的順序型(sequential)數據結構。一個壓縮列表可以包含任意多個節點(entry),每個節點可以保存一個字節數組或者一個整數值。

壓縮列表的各個組成部分:

示例:

0x3c(十六進制):3*16+12 = 60(十進制)

7.2 壓縮列表節點的構成

       每個壓縮列表節點可以保存一個字節數組或者一個整數值。

       字節數組可以是以下三種長度的其中一種:

       而整數值可以是以下6種長度的其中一種:

        每個壓縮列表節點都由previous_entry_length、encoding、content三個部分組成:

7.2.1 previous_entry_length

       節點的previous_entry_length屬性以字節爲單位,記錄了壓縮列表中前一個節點的長度。previous_entry_length 屬性的長度可以是1字節或者5字節:

  1. 如果前一節點的長度小於254字節,那麼previous_entry_length屬性的長度爲1字節;
  2. 如果前一節點的長度大於等於254字節,那麼previous_entry_length屬性的長度爲5字節:其中屬性的第一字節會被設置爲0xFE(十進制值254),之後的4個字節則用於保存前一節點的長度。

       因爲節點的previous_entry_length屬性記錄了前一節點的長度,所以程序可以通過指針運算,根據當前節點的起始地址來計算出前一節點的起始地址。(壓縮列表從表尾向表頭遍歷操作就是使用這一原理實現的)

7.2.2 encoding

       節點的encoding屬性記錄了節點的content屬性所保存數據的類型以及長度:

  1. 一字節、兩字節或者五字節長,值的最高位爲00、01或者10的是字節數組編碼:這種編碼表示節點的content屬性保存着字節數組,數組的長度由編碼除去最高兩位之後的其他位記錄;
  2. 一字節長,值的最高位以11開頭的是整數編碼:這種編碼表示節點的content屬性保存着整數值,整數值的類型和長度由編碼除去最高兩位之後的其他位記錄;

所有可用的字節數組編碼:(表格中下劃線”_”表示留空,而b、x等變量代表實際的二進制數據)

所有可用的整數編碼:

7.2.3 content

        節點的content屬性負責保存節點的值,節點值可以是一個字節數組或者整數,值的類型和長度由節點的encoding屬性決定。

上圖中,編碼的最高兩位00表示節點保存的是一個字節數組;編碼的後6位001011記錄了字節數組的長度11;

7.3 連鎖更新

        考慮這樣一種情況:在一個壓縮列表中,有多個連續的、長度介於250字節到253字節(皆小於254字節)之間的節點e1至eN。

        因爲e1至eN的所有節點的長度都小於254字節,所以記錄這些節點的長度只需要1字節長的previous_entry_length屬性。

        這時,如果我們將一個長度大於等於254字節的新節點new設置爲壓縮列表的表頭節點,那麼new將成爲e1的前置節點。

        e1節點的previous_entry_length屬性僅長1個字節,沒辦法保存新節點new的長度,所以程序將對壓縮列表執行空間重分配操作,並將e1節點的previous_entry_length屬性從原來的1字節擴展爲5字節長。

       由於e1的長度增加,又會導致後續節點的previous_entry_length增加,如此一直擴展下去,程序需要不斷地對壓縮列表執行空間重分配操作,直到eN爲止。

       Redis將這種在特殊情況下產生的連續多次空間擴展操作稱爲“連鎖更新(cascade update)”。

       除此之外,刪除節點也有可能會引發連鎖更新。

       考慮如果small、e1-eN都是大小介於250字節至253字節的節點,big節點的長度大於等於254字節。

       因爲連鎖更新在最壞情況下需要對壓縮列表執行N次空間重分配操作,而每次空間重分配的最壞複雜度爲O(N),所以連鎖更新的最壞複雜度爲O(N2)。

       注意,儘管連鎖更新的複雜度較高,但是真正造成性能問題的機率是很低的,ziplistPush等命令的平均複雜度僅爲O(N)。

      注:redis 3.2以後,quicklist作爲列表鍵的實現底層實現之一,代替了壓縮列表。

7.4 壓縮列表API

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章