redis 中的壓縮列表是由一系列特殊編碼的連續記憶體塊組成的順序型資料結構。列表中每個節點可以儲存乙個位元組陣列或者乙個整數值。
它的存在主要是為了節約記憶體。
壓縮列表在 redis 中主要作為了 list 列表和 hash 雜湊兩種資料結構的底層實現之一。
在 list 列表中,要是列表中的儲存元素數量少且每個元素是小的整數值或者長度較短的字串,那麼列表將使用壓縮列表結構作為底層實現。
在 hash 雜湊中,要是雜湊中的儲存鍵值對數量少且每個鍵值對的鍵和值是小的整數值或者長度較短的字串,那麼雜湊將使用壓縮列表結構作為底層實現。
乙個大概壓縮列表的結構示意圖:
屬性 zlbytes
屬性 zlbytes 占用4個位元組的長度,儲存了整個壓縮列表占用的總位元組數。
在對壓縮列表重新進行記憶體分配或者計算 zlend 屬性值的時候會用到。
屬性 zltail
屬性 zltail 占用了4個位元組的長度,儲存了壓縮列表表尾節點距離壓縮列表的開始位址有多少個具體位元組。
通過這個屬性值,程式不用遍歷整個壓縮列表就可以在時間複雜度為 o(1) 的情況下,直接確定表尾節點的位置。
屬性 zllen
屬性 zltail 占用了2個位元組的長度,儲存了壓縮列表節點的數量。
需要注意的是,當 zllen 的值為小於uint16_max (65535) 的時候,這個值就是真實的壓縮列表節點數量,大於的時候,節點的真實數量是需要遍歷整個壓縮列表才能計算得出。
屬性 zlend
屬性 zlend 占用了1個位元組的長度,主要用於表示它是在壓縮列表的最後位置。
屬性 entry
屬性 entry 是作為壓縮列表的節點,下面我們具體介紹壓縮列表節點 entry。
壓縮列表節點結構抽象**:
typedef struct zlentry zlentry;
這是乙個抽象的壓縮列表節點結構,實際儲存並不是這樣子的結構,只是這樣設計到時候訪問相關操作方便。
屬性 prevrawlen、prevrawlensize
屬性 prevrawlen 為前置節點的長度,而 prevrawlensize 是代表儲存 prevrawlen 這個屬性所需的位元組大小
屬性 len、lensize
屬性 len 為當前節點值的長度,而 lensize 是代表儲存 len 屬性所需的位元組大小。(注意:當節點儲存的是字串的時候,len 為字串長度,如果儲存的是整數值,len 為整數值的位元組長度)
屬性 headersize
屬性 headersize 為當前節點 header 的長度,等於 prevrawlensize + lensize
屬性 encoding
屬性 encoding 為當前節點值所使用的編碼型別
屬性 p
屬性 p 為乙個指標,指向了當前節點的記憶體位址
壓縮列表節點具體儲存結構
乙個壓縮列表節點具體的結構應該是這樣子的:
屬性 previous_entry_length 儲存了前置節點的長度。
程式主要可以通過這個前置節點的長度,以及根據當前節點的起始位址來計算出前置節點的起始位址。壓縮列表的從表頭到表尾的遍歷操作就是通過這樣的原理來實現的。
encoding
屬性 encoding 記錄了對應節點的 content 屬性所儲存資料的型別以及長度。
encoding 的長度可能是乙個位元組、兩個位元組或者五個位元組。它具體多少個位元組,取決於 encoding 值的最高兩位。當最高兩位為00、01、10開頭的時候,代表 content 儲存的是字串且 encoding 的長度分別為乙個位元組、兩個位元組或者五個位元組。而為11開頭的時候,即 content 儲存的是整數且 encoding 的長度為乙個位元組。
00開頭的時候:
encoding 占用了乙個位元組的空間,表示 content 能儲存長度小於等於 2^6-1 位元組的字串
01開頭的時候:
encoding 占用了兩個位元組的空間,表示 content 能儲存長度小於等於 2^14-1 位元組的字串
10開頭的時候:
encoding 占用了五個位元組的空間,第一位元組的後六位由0填充,剩下的表示 content 能儲存長度小於等於 2^32-1 位元組的字串
11開頭的時候:
encoding 占用了乙個位元組的空間,且由於編碼的不同,content 儲存的整數值範圍都是不同的
content
屬性 content 儲存了壓縮列表節點的值,節點值可以是乙個字串或者是整數值,值的型別和長度由 encoding 屬性值決定
此時我們把乙個長度大於等於254位元組的新節點 new 設定到壓縮列表的表頭的時候,那麼 new 節點將成為 e1 節點的前置節點,而 e1 節點的 previous_entry_length 值只有1位元組的長度,沒有儲存表示前置節點長度的5個位元組長度,所以程式分配空間給 e1 多四個位元組儲存,接著 e2 也需要增加空間儲存 e1 的長度,然後以此類推,引發了後面所有的節點需要更新空間,這就是連鎖更新。
連鎖更新會導致在最壞的情況下要對壓縮列表 n 次空間重新分配,而每次空間重新分配的複雜度為 o(n),所以連鎖更新的最壞時間複雜度為 o(n^2)
不過這種是需要恰好多個連續節點都是在 250-253位元組之間,連鎖更新才可能發生,這種情況概率很低。又假如是幾個連續這樣的 250-253 位元組之間,對效能是不會造成影響的。
先對 redis 壓縮列表的含義、應用場景講述,了解了壓縮列表主要是為了節約記憶體的資料結構,再接著對其資料結構的詳細了解以及其主要的連鎖更新操作,深入了解了壓縮列表的底層。
參考:《 redis設計與實現 》
Redis 底層資料結構 壓縮列表
壓縮列表 ziplist 是列表鍵和雜湊鍵的底層實現之一。當列表鍵只包含少量列表項,並且每個列表項或者是小整數值,或者是長度比較短的字串,redis會使用壓縮列表作為列表鍵的底層實現。當雜湊鍵只包含少量鍵值對,並且每個鍵值對的鍵和值,要麼是小整數值,要麼是長度比較短的字串,redis會使用壓縮列表作...
Redis底層資料結構 六 壓縮列表
壓縮列表 ziplist 是列表鍵和雜湊鍵的底層實現之一。當乙個列表鍵只包含少量列表項,並且每個列表項要麼是小整數值,要麼就是長度比較短的字串,redis就會使用壓縮列表來做列表鍵的底層實現。壓縮列表是redis為了節約記憶體而開發的,是由一系列編碼的聯絡記憶體塊組成的順序性資料結構。乙個壓縮列表可...
redis 底層資料結構 壓縮列表 ziplist
壓縮列表是列表鍵和雜湊鍵的底層實現之一。當乙個列表鍵只包含少量列表項,並且每個列表項要麼就是小整數,要麼就是長度比較短的字串,redis就會使用壓縮列表來做列表鍵的底層實現 當乙個雜湊鍵只包含少量鍵值對,並且每個鍵值對的鍵和值要麼就是小整數值,要麼就是長度比較短的字串,那麼redis就會使用壓縮列表...