redis源碼之壓縮列表ziplist

未完待續…

壓縮列表ziplist

1.簡介

連續,無序的數據結構。壓縮列表是 Redis 爲了節約內存而開發的, 由一系列特殊編碼的連續內存塊組成的順序型(sequential)數據結構

2.組成

這裏寫圖片描述

屬性 類型 長度 用途
zlbytes uint_32t 4B 記錄整個壓縮列表佔用的內存字節數:在對壓縮列表進行內存重分配, 或者計算 zlend的位置時使用
zltail uint_32t 4B 記錄壓縮列表表尾節點距離壓縮列表的起始地址有多少字節:通過這個偏移量,程序無須遍歷整個壓縮列表就可以確定表尾節點的地址。
zllen uint_16t 2B 記錄了壓縮列表包含的節點數量: 當這個屬性的值小於UINT16_ MAX (65535)時, 這個屬性的值就是壓縮列表包含節點的數量; 當這個值等於 UINT16_MAX 時, 節點的真實數量需要遍歷整個壓縮列表才能計算得出。
entryX 列表節點 不定 壓縮列表包含的各個節點,節點的長度由節點保存的內容決定
zlend uint_8t 1B 特殊值 0xFF (十進制 255 ),用於標記壓縮列表的末端

3.壓縮列表節點的構成

一個壓縮列表可以包含任意多個節點(entry), 每個節點可以保存一個字節數組或者一個整數值(小整數值或者長度比較短的字符串)。
這裏寫圖片描述
(1)節點的 previous_entry_length 屬性以字節爲單位, 記錄了壓縮列表中前一個節點的長度
(1)如果前一節點的長度小於 254 字節, 那麼 previous_entry_length 屬性的長度爲 1 字節: 前一節點的長度就保存在這一個字節裏面。例如:值爲0x05
(2)如果前一節點的長度大於等於 254 字節, 那麼 previous_entry_length 屬性的長度爲 5 字節: 其中屬性的第一字節會被設置爲 0xFE (十進制值 254), 而之後的四個字節則用於保存前一節點的長度。例如:值爲0xFE00002766;0xFE表明這是一個5字節長的屬性,之後的四個字節 0x00002766(10086)纔是前一節點的實際長度。
程序可以通過指針運算, 根據當前節點的起始地址來計算出前一個節點的起始地址。
(2)節點的 encoding 屬性記錄了節點的 content 屬性所保存數據的類型以及長度:
一字節(00)、兩字節(01)或者五字節長(10), 值的最高位爲 00 、 01 或者 10 的是字節數組編碼: 這種編碼表示節點的 content 屬性保存着字節數組, 數組的長度由編碼除去最高兩位之後的其他位記錄;
一字節長, 值的最高位以 11 開頭的是整數編碼: 這種編碼表示節點的 content 屬性保存着整數值, 整數值的類型和長度由編碼除去最高兩位之後的其他位記錄;

字節數組編碼 編碼長度 content屬性保存的值
00bbbbbb 1B 長度小於等於63 字節的字節數組
01bbbbbb xxxxxxxx 2B 長度小於等於16 383 字節的字節數組
10______ aaaaaaaa bbbbbbbb cccccccc dddddddd 5B 長度小於等於 4 294 967 295 的字節數組
整數編碼 編碼長度 content屬性保存的值
11000000 1B int16_t 類型的整數
11010000 1B int32_t 類型的整數
11100000 1B int64_t 類型的整數
11110000 1B 24 位有符號整數
11111110 1B 8 位有符號’些數
1111xxxx 1B 使用這一編碼的節點沒有相應的content 屬性,因爲編碼本身的xxxx 四個位已經保存了一個介於0 和12 之間的值,所以它無須content 屬性

(3)節點的 content 屬性負責保存節點的值, 節點值可以是一個字節數組或者整數, 值的類型和長度由節點的 encoding 屬性決定。
這裏寫圖片描述

3.“連鎖更新”

前面說過,每個節點的previous_entry _length 屬性都記錄了前一個節點的長度:
(1)如果前一節點的長度小於254 字節,那麼previ ous_ entry_length 屬性需要用
1字節長的空間來保存這個長度值。
(2)如果前一節點的長度大於等於254 字節,那麼previous entry length 屬性需
要用5 字節長的空間來保存這個長度值。
如果我們將一個長度大於等於 254 字節的新節點 new 設置爲壓縮列表的表頭節點,那麼麻煩的事情來了,由於previous entry length大小不夠用(1->5B),後面所有的節點可能都要重新分配內存大小。因爲連鎖更新在最壞情況下需要對壓縮列表執行 N 次空間重分配操作, 而每次空間重分配的最壞複雜度爲 O(N) , 所以連鎖更新的最壞複雜度爲 O(N^2) 。

但是呢,儘管連鎖更新的複雜度較高,但它真正造成性能問題的機率是很低的。
(1)首先,壓縮列表裏要恰好有多個連續的、長度介於250 字節至253 宇節之間的節點,連鎖更新纔有可能被引發,在實際中,這種情況並不多見;
(2)其次,即使出現連鎖更新,但只要被更新的節點數量不多,就不會對性能造成任何影響:比如說,對三五個節點進行連鎖更新是絕對不會影響性能的;
因爲以上原因, ziplistPush 等命令的平均複雜度僅爲0(的,在實際中,我們可以放心地使用這些函數,而不必擔心連鎖更新會影響壓縮列表的性能。

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