ziplist的佈局以下,全部的字符默認使用小端序保存:git
+--------+--------+--------+--------+-------+-------+-------+ |zlbytes | zltail | zllen | entry | ... | entry | zlend | +--------+--------+--------+--------+-------+-------+-------+
uint32_t zlbytes
:爲一個無符號整數。保存了ziplist佔用的字節數,包含zlbytes字段自己佔用的4個字節。主要用於調整數據結構的大小。github
uint32_t zltail
:最後一個entry的字節偏移量(非zlend)。用於從list的另外一端執行pop操做(即倒序遍歷)redis
uint16_t zllen
:entry的數目。當保存的entry大於216-2個entry時,則將該值設置爲216-1,此時須要遍歷整個entry list來計算list中的entry數目數據結構
uint8_t zlend
:表示ziplist中的最後一個entry。字節編碼等同於255(即FF)。表示ziplist的結束符佈局
ziplist中的每一個entry都使用一個元數據做爲前綴,該元數據包含兩部分的信息:首先保存了前一個entry的長度,用於倒序查找;再者保存了entry的編碼類型,表示entry的類型,如整數或字符串,當編碼類型爲字符串時,該字段也表示了字符串的長度。字符串的entry-data的長度就等同於該字符串的長度,而整數的entry-data的長度須要根據編碼類型進行判斷,並不必定等同於其entry-data字符串的長度(見下文encoding)。一個完整的entry爲:ui
+--------+--------+----------+ |prevlen |encoding|entry-data| +--------+--------+----------+
有時編碼類型即表示entry自己(例如小的整數),這種狀況下會忽略entry-data
字段,此時entry變爲:編碼
+--------+--------+ |prevlen |encoding| +--------+--------+
prevlen
表示前一個entry的長度,使用以下方式進行編碼:當前一個entry的長度小於254(255是個特殊字符,被zlend
使用)字節時,該字段會使用一個字節(即8 bit)表示長度;當長度大於或等於254時,將會使用5個字節,此時第一個字節會被設置爲254(FE)來表示一個較大的數值,後續4個字節表示前面一個entry的長度。code
所以,prevlen
的編碼爲:ip
若是前一個entry的長度小於254,編碼爲:字符串
+-------+--------+-----+ |prevlen|encoding|entry| +-------+--------+-----+
若是前一個entry的長度大於254,編碼以下:
+----+---------------+--------+-----+ |0xFE|4 bytes prevlen|encoding|entry| +----+---------------+--------+-----+
entry
的encoding
字段取決於entry
的內容。當entry
爲字符串時,encoding
的第一個字節的前2bit保存了編碼類型,剩餘的bit位表示字符串的長度。當entry爲整數時,encoding
僅佔用1個字節,encoding
的前2bit都設置爲1,後續的2bit用於指定整數的類型,如int16_t,int32_t。encoding
中的第一個字節老是用於斷定entry的類型。舉例以下:
* |00pppppp| - 1 byte * 字符串的長度小於或等於63字節(6 bits). * "pppppp" 表示6bit長度的無符號整數. * |01pppppp|qqqqqqqq| - 2 bytes * 字符串的長度小於或等於16383字節(14 bits). * IMPORTANT: 14 bit的數字使用大端序保存. * |10000000|qqqqqqqq|rrrrrrrr|ssssssss|tttttttt| - 5 bytes * 字符串的長度大於或等於16384字節,只使用第1個字節以後的4個字節表示長度,最大爲32^2-1,第一個 * 字節的低6位沒有使用,設置爲0。所以entry的最大長度爲32 * IMPORTANT: 32 bit的數字使用大端序保存. * |11000000| - 3 bytes * 整數編碼爲int16_t (2 bytes). * |11010000| - 5 bytes * 整數編碼爲int32_t (4 bytes). * |11100000| - 9 bytes * I整數編碼爲int64_t (8 bytes). * |11110000| - 4 bytes * 編碼爲24 bit的有符號整數 (3 bytes). * |11111110| - 2 bytes * 編碼爲8 bit的有符號整數 (1 byte). * |1111xxxx| - (xxxx 取值爲 0000 到 1101) 表示4bit的整數 * 無符號整數的取值爲0到12,因爲沒法使用0000(被|11110000|編碼佔用)和1111(被zlend佔用),所以取值 * 爲1到13,所以須要從低4位的整數減去1得到entry的值. * |11111111| - 表示ziplist的終止entry,即zlend
以下ziplist包含2個元素,表示字符串"2"和"5",長度爲15字節,能夠看到因爲數值小於13,其編碼和數值放在了一個字節中。
[0f 00 00 00] [0c 00 00 00] [02 00] [00 f3] [02 f6] [ff] | | | | | | zlbytes zltail entries "2" "5" end
前4個字節(zlbytes
)表示15,即整個ziplist包含的字節數;第2個4字節(zltail
)最後一個entry的字節偏移,即字符串爲"5"的entry的位置,偏移量爲12字節;接下來的16bit(entries
)表示ziplist中的entry的數目,爲2;"00 f3"表示list中的第一個entry "2",它包含了前一個entry的長度(prevlen
),爲0,"f3"對應的編碼爲"|1111xxxx|","xxxx"的取值爲0001到1101,去除前4個bit "1111",並減去1,獲得entry的值爲2。下一個entry的prevlen
爲2,表示前一個entry佔用了2字節."f6"的編碼與前一個相同,去除前4個bit,並減去1,獲得entry的值爲5;最後的"ff"表示ziplist的結束(zlend
)。
在上述ziplist中追加一個"Hello World"的entry的編碼。第一個字節表示前面entry的長度,第二個字節表示encoding,二進制爲"|00pppppp|",所以"0b"表示一個11字節的字符串。從第3個字節(48)到最後一個字節(64)表示ASCII編碼的字符串"Hello World"。
[02] [0b] [48 65 6c 6c 6f 20 57 6f 72 6c 64]
源碼解析參見:ziplist.c