1. 程式人生 > 其它 >壓縮列表 - 《Redis設計與實現》讀書筆記

壓縮列表 - 《Redis設計與實現》讀書筆記

使用場景

  1. 當一個列表鍵只包含少量列表項,並且每個列表項要麼就是小整數值,要麼就是長度比較短的字串,使用壓縮列表實現列表鍵
  2. 當一個雜湊鍵只包含少量鍵值對,並且每個鍵值對的鍵和值要麼就是小整數值,要麼就是長度比較短的字串,使用壓縮雜湊實現雜湊鍵

定義

// 壓縮列表是為了節約記憶體而開發出由 一系列特殊編碼的 連續記憶體塊組成的 順序型資料結構,一個壓縮列表可以包含任意多個節點
// 壓縮列表組成部分: zlbytes   zltail  zllen   entry1  entry2 ···· entryN  zlend
// zlbytes : 型別為uint32_t,長度為4個位元組,記錄整個壓縮列表佔用的記憶體位元組數
// zltail : 型別為uint32_t,長度為4個位元組,記錄壓縮列表表尾節點距離壓縮列表的起始地址有多少位元組
// zllen : 型別為uint16_t,長度為2個位元組,記錄壓縮列表包含的節點數量
// entryX : 列表節點,記錄壓縮列表包含的各個節點,節點的長度由節點儲存的內容決定
// zlend : 型別為uint8_t,長度為1個位元組,特殊值0xFF(即255),用於標記壓縮列表的末端

// 壓縮列表節點:每個節點可以儲存一個字串或者一個整數值
typedef struct {
    // 當儲存的是位元組陣列,sval儲存字串值,slen儲存字串的長度
    unsigned char *sval;
    unsigned int slen;

    // 當儲存的是整數值,lval儲存整數值,sval為NULL
    long long lval;

    // sval、lval這兩個屬性都由previous_entry_length、encoding、content三個部分組成
    // previous_entry_length : 以位元組為單位,記錄前一個節點的長度,可以憑此進行指標運算,從而實現遍歷操作
    // encoding : 記錄content所儲存資料的型別以及長度
    // content : 儲存節點的值

} ziplistEntry;

連續更新

每個節點的previous_entry_length都記錄了前一個節點的長度:
如果前一節點的長度 < 254位元組,那麼previous_entry_length需要用1位元組長的空間儲存長度值
如果前一節點的長度 >= 254位元組,那麼previous_entry_length需要用5位元組長的空間儲存長度值

當新增新節點、刪除節點的時候,前一個節點的長度值從 小於254位元組 => 大於等於254位元組
從而引發下一個節點的previous_entry_length需要從1位元組擴充套件為5位元組,
進而引發後續節點的previous_entry_length也需要從1位元組擴充套件為5位元組,
這種特殊情況下產生的連續多次空間擴充套件操作稱之為 連鎖更新

,最壞複雜度為O(N^2)

要引發連鎖更新的機率極低

  1. 壓縮列表裡要恰好有多個連續的、長度介於250位元組至253位元組之間的節點,連鎖更新才有可能被引發,在實際中,這種情況並不多見
  2. 即使出現連鎖更新,但只要被更新的節點數量不多,就不會對效能造成影響,

原始碼閱讀

  1. 檔案:src/ziplist.h 和 src/ziplist.c
隻言片語任我說,提筆句句無需忖。落筆不知寄何人,唯有邀友共斟酌。