天天看點

Redis底層資料結構之ziplist(壓縮清單)

ziplist是Redis中的某些資料類型底層所使用的資料結構

Redis中的hash,List,Sorted List這幾種類型的資料在某些情況下會使用ziplist來存儲。

Hash類型

當hash類型的資料滿足以下條件時,底層使用ziplist存儲。

  1. 當hash鍵值對個數小于等于 hash-max-ziplist-entries 配置的值,預設512
  2. 當鍵值對中值的長度小于等于 hash-max-ziplist-value 配置的值,預設64

當hash類型的資料同時滿足以上兩個條件時,會将value采用ziplist來存儲。

Redis底層資料結構之ziplist(壓縮清單)
Redis底層資料結構之ziplist(壓縮清單)

List類型

redis中list類型的資料底層是使用quicklist來存儲的,而quicklist是基于linkedlist + ziplist實作的。

Redis底層資料結構之ziplist(壓縮清單)

Sorted Set類型

當Sorted Set類型的資料滿足以下條件時,底層使用ziplist存儲。

  1. 當元素個數小于等于 zset-max-ziplist-entries配置的值,預設128
  2. 每個元素的值小于等于 zset-max-ziplist-value配置

當Sorted Set類型的資料同時滿足以上兩個條件時,會将value采用ziplist來存儲。

Redis底層資料結構之ziplist(壓縮清單)
Redis底層資料結構之ziplist(壓縮清單)

ziplist的記憶體布局

ziplist是由一系列特殊編碼的連續記憶體塊組成的順序存儲結構,類似于數組,ziplist在記憶體中是連續存儲的,但是不同于數組,為了節省記憶體 ziplist的每個元素所占的記憶體大小可以不同,每個節點可以用來存儲一個整數或者一個字元串。

ziplist類似于雙向連結清單,但是它不存儲上一個節點和下一個節點的指針,而是存儲上一個節點長度和目前節點長度,通過犧牲部分讀寫性能,來換取高效的記憶體空間使用率,節約記憶體。

Redis底層資料結構之ziplist(壓縮清單)
  • zlbytes:記錄了壓縮清單占用的記憶體位元組數,在對壓縮清單進行記憶體重配置設定,或者計算zlend的位置時使用。它本身占了4個位元組。
  • zltail:記錄了尾節點(entry)至起始節點(entry)的偏移量。通過這個偏移量,可以快速确定最後一個entry節點的位址。
  • zllen:記錄了entry節點的數量。當zllen的值小于65535時,這個值就表示節點的數量。當zllen的值大于65535時,節點的真實數量需要周遊整個壓縮清單才能得出。
  • entry:壓縮清單中所包含的每個節點。每個節點的長度根據該節點的内容來決定。
  • zlend:特殊值0XFF,标記了壓縮清單的末端。表示該壓縮清單到此為止。

值得注意的是,這個壓縮清單的記憶體空間是連續的。這也是壓縮清單的主要特點,空間連續,避免記憶體碎片,節省記憶體。

entry是連結清單中的一個節點,代表了一個資料。

redis中對壓縮清單中節點的定義如下:

typedef struct zlentry {
    unsigned int prevrawlensize; /*存儲上一個節點長度的數值所需要的位元組數*/
    unsigned int prevrawlen;     /* 上一個節點的長度 */
    unsigned int lensize;        /* 目前節點長度的數值所需要的位元組數*/
    unsigned int len;            /* 目前節點的長度 */
    unsigned int headersize;     /* 目前節點的頭部大小,值 = prevrawlensize + lensize. */
    unsigned char encoding;      /* 編碼方式,ZIP_STR_* 或 ZIP_INT_* */
    unsigned char *p;            /* 指向節點内容的指針. */
} zlentry;
           

雖然定義了這個結構體,但是根本就沒有使用zlentry結構來作為壓縮清單中用來存儲資料節點中的結構,因為,這個結構存小整數或短字元串實在是太浪費空間了。這個結構總共在32位機占用了28個位元組(32位機),在64位機占用了32個位元組。這不符合壓縮清單的設計目的:提高記憶體的使用率。是以,在redis中,并沒有定義結構體來進行操作,而是定義了一些宏,壓縮清單的節點真正的結構如下圖所示:

Redis底層資料結構之ziplist(壓縮清單)
  • prev_entry_len:記錄前驅節點的長度。
  • encoding:記錄目前節點的value成員的資料類型以及長度。
  • value:根據encoding來儲存位元組數組或整數。

具體的含義,要深入了解的話,參考:ziplist源碼剖析

ziplist的特點

  • 壓縮清單ziplist結構本身就是一個連續的記憶體塊,由表頭、若幹個entry節點和壓縮清單尾部辨別符zlend組成,通過一系列編碼規則,提高記憶體的使用率,使用于存儲整數和短字元串。
  • 壓縮清單ziplist結構的缺點是:每次插入或删除一個元素時,都需要進行頻繁的進行記憶體的擴充或減小,然後進行資料”搬移”,甚至可能引發連鎖更新,造成嚴重效率的損失。

繼續閱讀