Redis數據結構(六) —壓縮列表

壓縮列表

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

1.壓縮列表的構成

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

在這裏插入圖片描述

屬性 類型 長度 用途
zlbytes unit32_t 4字節 記錄整個壓縮列表佔用的內存字節數, 在對壓縮列表進行內存重分配,或者計算zlend時使用
zltail unit32_t 4字節 記錄壓縮列表表尾節點距離壓縮列表的的起始地址有多少字節, 通過這個偏移量, 程序無須遍歷整個壓縮列表就能確定表尾節點的地址
zllen unit16_t 2字節 記錄了壓縮列表包含的節點數量, 當小於UINT16_MAX時,是真正的大小,若超過這個值, 則需要遍歷整個壓縮列表才能得出
entryX 列表節點 不定 壓縮列表包含的各個節點, 節點的長度由節點保存的內容決定
zlend unit8_t 1字節 特殊值0xFF, 用於標記壓縮列表的末端

2.壓縮列表的構成

每個壓縮列表可以保存一個字節數組或者一個整數值, 其中, 字節數組可以是以下三種長度之一 :

  • 長度小於等於63(2^6-1)字節的字節數組
  • 長度小於等於16383(2^14-1)字節的字節數組
  • 長度小於等於4294967295(2^32-1)字節的字節數組

而整數值則可以使以下六種長度之一:

  • 四位長,介於0至12間的無符號整數
  • 1字節長的有符號整數
  • 3字節長的有符號整數
  • int16_t類型整數
  • int32_t類型整數
  • int64_t類型整數

每個壓縮列表節點都由previous_entry_length, encoding, content三個部分組成
在這裏插入圖片描述

1> previous_entry_length

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

  • 如果前一節點的長度小於254字節, 那麼previous_entry_length長度 爲1字節, 前一節點的長度就保存在這一個字節裏面
  • 如果前一節點的長度大於等於253字節, 那麼previous_entry_length屬性 的長度就爲5字節 : 其中屬性的第一字節會被設置爲0xFE(254),而之後的四個字節則用於保存前一節點的長度

通過previous_entry_length屬性, 可以根據一個節點的地址減去該節點的previous_entry_length值來得到這個節點前一個節點的起始地址. 壓縮列表從表尾向表頭遍歷操作就是用這一原理實現的

2> encoding

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

  • 一字節,兩字節或者五字節長, 值的最高位爲00,01或者10的是字節數組 : 這種編碼表示節點的 content屬性保存着字節數組, 數組的長度由編碼取出最高兩位之後的其他位記錄

  • 一字節, 值的最高位是以11開頭的是整數編碼 : 這種編碼表示節點的content屬性保存着整數值, 整數值的類型和長度由編碼出去最高兩位之後的其他位記錄

3> content

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

在這裏插入圖片描述

3.連鎖更新

前面說到過, previous_entry_length屬性都記錄了前一個節點的長度, 可能是一字節或者五字節, 這時如果在某一個所有節點的大小都爲250-253字節的壓縮列表中, 在表頭位置插入了一個長度大於254字節的節點, 引起其後方第一個節點的previous_entry_length從一字節更新爲五字節, 這時此節點的大小也超過了254字節, 從而持續繼續引發後面節點的更新, Redis將在這種特殊情況下產生的多次空間擴展操作稱之爲"連鎖更新". 除了添加新節點可能會引發連鎖更新之外, 刪除節點也可能會引發連鎖更新

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

需要注意的是, 儘管連鎖更新的複雜度較高, 但它真正造成性能問題的機率是很低的:

  • 首先,壓縮列表裏要恰好有多個連續的, 長度介於250字節至253字節之間的節點, 連鎖更新纔有可能被引發, 在實際中, 這種情況並不常見
  • 其次, 即使出現連鎖更新, 但只要被更新節點的數量不多, 就不會造成任何影響

基於以上原因, zskiplist等命令的平均複雜度僅爲O(N), 在實際中, 我們可以放心的使用這些函數, 而不必擔心連鎖更新會影響壓縮列表的性能.

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