A. 【Redis】基礎數據結構-ziplist壓縮列表
壓縮列表是列表和哈希表的底層實現之一:
Redis壓縮列表是由連續的內存塊組成的列表,主要包含以下內容:
列表在初始化的時候會計算需要分配的內存空間大小,然後進行內存分配,之後將內存空間的最後一個位元組標記為列表結尾,內存空間的大小計算方式如下:
所以在創建之後,內存布局如下,此時壓縮列表中還沒有節點:
之後如果如果需要添加節點,會進行移動,為新節點的插入騰出空間,所以還是斗彎余佔用的連續的空間:
壓縮列表的節點可以存儲字元串或者整數類型的值,為了節省內存,它採用了變長的編碼方式,壓縮列表的節點的結構定義如下:
prevrawlen :存儲前一個節點的長度(佔用的位元組數),這樣如果從後向前遍歷,只需要當前節點的起始地址減去長度的偏移量prevrawlen就可以定位到上一個節點的位置,prevrawlen的長度可以是1位元組或者5位元組:
encoding :記錄了節點的數據類型和內容的長度,因為壓縮列表可以存儲字元串或者整型,所以有以下兩種情況:
存儲內容為整數時,encoding佔用1個位元組,最高位是11開頭,後六位代表整數值的長度,其中當編碼為1111xxxx時情況比較特殊,
後四位的值在0001和1101之間, 此時直接代表數據的內容,是0到12之間的一個數字 ,並不是數據長度,因為它代表了數據內容,所以也不需要額外的空間存儲數據內容。
zipStoreEntryEncoding
因為壓縮列表中每個節點記錄了前一個節點的長度:
假設有一種情況,一個壓縮列表中,存儲了多個長度是253位元組的節點,因為節點的長度都在254位元組以內,所以每個節點的prevrawlen只需要1個位元組去存儲長度的值:
此時在列表的頭部需要新增加一個節點,並且節點的長度大於254,這個時候原先的頭結點entry1 prevrawlen使用1位元組已經不能滿足當前的情況了,必須要使用5位元組存儲,因此entry1的prevrawlen變成了5位元組,entry1的長度也會跟著增加4個位元組,已經超過了254位元組,因為大於254就需要使用5個位元組存儲,所以entry2的prevrawlen也鬧李需要改變為5位元組,後面的以此類推,引發了連鎖更新,這種情況稱之為連鎖更新:
總結空滾
(1)Redis壓縮列表使用了一塊連續的內存,來節約內存空間。
(2)壓縮列表的節點可以存儲字元串或者整數類型的值,它採用了變長的編碼方式,根據數據類型的不同以及數據長度的不同,選擇不同的編碼方式,每種編碼佔用的位元組大小不同,以此來節約內存。
(3)壓縮列表的每個節點中存儲了前一個節點的位元組長度,如果知道某個節點的地址,可以使用地址減去位元組長度定位到上一個節點,不過新增節點的時候,由於前一個節點的長度大於254使用5個位元組,小於254使用1個位元組存儲,在一些極端的情況下由於長度的變化會引起連鎖更新。
參考
黃健宏《Redis設計與實現》
極客時間 - Redis源碼剖析與實戰(蔣德鈞)
【張鐵蕾】Redis內部數據結構詳解(4)——ziplist
【_HelloBug】Redis-壓縮表-__ziplistInsert詳解
圖解Redis之數據結構篇——壓縮列表
Redis版本:redis-6.2.5