1. 程式人生 > 其它 >探祕 Redis 壓縮列表

探祕 Redis 壓縮列表

技術標籤:redis列表redis資料結構

壓縮列表,即ziplist,是列表(list),雜湊(hash)和有序集合(zset)的底層實現之一,Redis 為了節約記憶體空間使用,在這些容器物件在元素個數較少的時候,採用壓縮列表 (ziplist) 進行儲存。
list示例:
在這裡插入圖片描述
zset示例:
在這裡插入圖片描述
hash示例:
在這裡插入圖片描述

壓縮列表的構成

壓縮列表是由一系列經過特殊編碼的連續的記憶體空間,元素之間緊挨著儲存,沒有任何冗餘空隙。一個壓縮列表可以包含任意的多個節點,每個節點可以儲存一個位元組陣列或者整數值。

struct ziplist<T> {
 int32 zlbytes; 
 int32 zltail_offset;
int16 zllength; T[] entries; int8 zlend; }

zlbytes 用於記錄整個壓縮列表佔用位元組數
zltail_offset 表示最後一個節點距離壓縮列表起始地址有多少個位元組,有個這個偏移量,無需遍歷就能快速定位到最後一個節點
zllength 表示壓縮列表裡的節點個數
entries 表示節點內容列表,挨個挨個緊湊儲存
zlend 標誌壓縮列表的結束,值恆為 0xFF
在這裡插入圖片描述

壓縮列表節點構成

struct entry {
 int<var> previous_entry_length; 
 int<var> encoding;
optional byte[] content; }

在這裡插入圖片描述

previous_entry_length 表示前一個 entry 的位元組長度,當壓縮列表倒著遍歷時,需要通過這個欄位來快速定位到下一個節點的位置。這個屬性長度可以是1位元組或者5位元組,如果前一個節點的長度大於或等於254位元組,那麼該屬性為5位元組,如果前一個節點長度小於254位元組,那麼該屬性為1位元組。

encoding 記錄了節點的content屬性的型別編碼,該屬性長度為1位元組,2位元組或者5位元組,值的最高位為00,01或者10的是位元組陣列的編碼,陣列的長度由該欄位除去最高兩位後的其他位表示,最高位為11的是整數編碼。

content 表示節點的內容,節點值可以是一個位元組陣列或者整數,型別由encoding屬性決定。

增加節點

由於 ziplist 是緊湊儲存的,如果想要插入一個新的節點可能就需要呼叫 realloc 擴充套件記憶體,將原有的內容拷貝到新的地址上去,也可能直接在原有的地址上進行擴充套件,具體會怎麼做取決於記憶體分配器演算法和當前的 ziplist 記憶體大小。

不過如果 ziplist 佔據記憶體太大,重新分配記憶體和拷貝記憶體就會有很大的消耗,所以 ziplist 一般不適合儲存大型字串和過多的元素。

級聯更新

由於每個節點的previous_entry_length記錄了前一個節點的長度資訊,而這個屬性的長度是不定的,可能是1位元組也可能是5位元組。
那麼如果有某個 entry 經過修改後變成從小於254位元組變成了大於254位元組,那麼這個 entry 的下一個節點的previous_entry_length屬性就要跟著更新,從1位元組擴充套件到5位元組,那麼這個更新可能也會導致當前 entry 變成了大於254 位元組,那麼它的下一個 entry 的previous_entry_length也要跟著更新,這就導致了級聯更新。

不過大家也不用太過於擔心,實際上這種情況發生的概率還是比較小的,因為它要求有連續多個 entry 長度介於250位元組到253位元組之間的節點才可能觸發,其次,只要更新的節點數量不是太多,也會對效能造成什麼影響。