ZipList是 Redis 中一種非常緊湊、節省內存的數據結構 Ziplist(壓縮列表) 的內部內存布局。它被用于存儲元素較少的 List、Hash 和 Zset。
下面我們來詳細介紹每一個節點的含義:
1. zlbytes
(ziplist bytes)
- 含義: 整個壓縮列表占用的總字節數。
- 類型: 32位無符號整數(
uint32_t
),固定占用 4 個字節。 - 作用: 這個字段記錄了 Ziplist 的總大小。有了它,程序無需遍歷整個列表就能知道其邊界,這在進行內存重分配(
realloc
)時非常有用,可以直接告訴內存管理器需要多大的空間。
2. zltail
(ziplist tail)
- 含義: 指向列表中最后一個元素(entry)的偏移量(offset)。
- 類型: 32位無符號整數(
uint32_t
),固定占用 4 個字節。 - 作用: 這個字段允許程序在 O(1) 的時間復雜度內定位到 Ziplist 的最后一個節點,而無需從頭遍歷。這使得從列表尾部進行 pop(彈出)等操作非常高效。
3. zllen
(ziplist length)
- 含義: 壓縮列表中的節點(entry)數量。
- 類型: 16位無符號整數(
uint16_t
),固定占用 2 個字節。 - 作用: 提供了在 O(1) 時間復雜度內獲取列表長度的能力。
- 特殊情況: 由于它只有16位,最大只能表示
65535
。如果 Ziplist 中的節點數超過或等于65535
,這個字段會被設置為特殊值65535
(FFFF
)。在這種情況下,要獲取真實長度,就必須遍歷整個列表來統計,時間復雜度會退化為 O(N)。不過,Ziplist 的設計初衷就是用于存儲少量數據,所以這種情況很少見。
4. entry
(列表節點)
- 含義: 這部分是 Ziplist 的核心,用來存儲實際的數據元素。圖中的
entry1
,entry2
,entry3
代表連續存放的多個節點。 - 結構: 每個
entry
自身的結構也非常精巧,它由三個部分組成:previous_entry_length
(前一節點的長度): 這個字段記錄了前一個節點所占用的總字節數。它的存在是實現從后向前遍歷 Ziplist 的關鍵。通過當前節點的地址減去這個長度,就可以得到前一個節點的起始地址。這個字段本身的長度是可變的(1字節或5字節),以節省空間。encoding
(編碼): 這個字段指明了content
部分的數據類型(是字符串還是整數)以及它的長度。編碼字段本身也是變長的,通過不同的二進制位模式來表示不同的編碼方式,實現了極致的空間優化。content
(內容): 實際存儲的數據,可以是整數或一個字節數組(字符串)。其長度由encoding
字段決定。
5. zlend
(ziplist end)
- 含義: 壓縮列表的結束標記。
- 類型: 8位無符號整數(
uint8_t
),固定占用 1 個字節。 - 值: 它的值永遠是
255
(二進制11111111
,十六進制0xFF
)。 - 作用: 作為一個明確的終止符,當程序遍歷列表時,一旦遇到這個值,就知道已經到達了列表的末尾。
總結
Ziplist 的設計精髓在于 “緊湊”。它不像常規鏈表那樣為每個節點都使用前后指針(在64位系統上,一個指針就占8字節),而是通過存儲前一節點的長度 (previous_entry_length
) 和利用連續內存布局,實現了雙向遍歷,從而極大地節省了內存。圖中 zlbytes
, zltail
, zllen
這三個頭部字段,共同為這個緊湊的結構提供了高效的宏觀操作能力。
下一篇:
(ZipList入門筆記二)為何ZipList可以實現內存壓縮,可以詳細介紹一下嗎