QuickList
**問題1:**ZipList雖然節省內存,但申請內存必須是連續空間,如果內存占用較多,申請內存效率很低。怎么辦?
- 為了緩解這個問題,我們必須限制ZipList的長度和entry大小。
**問題2:**但是我們要存儲大量數據,超出了ZipList最佳的上限該怎么辦?
- 我們可以創建多個ZipList來分片存儲數據
**問題3:**數據拆分后比較分散,不方便管理和查找,這多個ZipList如何建立聯系?
- Redis在3.2版本引入了新的數據結構QuickList,它是一個雙端鏈表,只不過鏈表中的每個節點都是一個ZipList。
為了避免QuickList中的每個ZipList中entry過多,Redis提供了一個配置項:list-max-ziplist-size來限制
-
如果值為正,則代表ZipList的允許的entry個數的最大值
-
如果值為負,則代表ZipList的最大內存大小,分5種情況:
- -1:每個ZipList的內存占用不能超過4kb
- -2:每個ZipList的內存占用不能超過8kb
- -3:每個ZipList的內存占用不能超過16kb
- -4:每個ZipList的內存占用不能超過32kb
- -5:每個ZipList的內存占用不能超過64kb
其默認值為 -2:
除了控制ZipList的大小,QuickList還可以對節點的ZipList做壓縮。
通過配置項list-compress-depth來控制。因為鏈表一般都是從首尾訪問較多,所以首尾是不壓縮的。這個參數是控制首尾不壓縮的節點個數:
- 0:特殊值,代表不壓縮
- 1:標示QuickList的首尾各有1個節點不壓縮,中間節點壓縮
- 2:標示QuickList的首尾各有2個節點不壓縮,中間節點壓縮
- 以此類推
默認值: 0
以下是QuickList的和QuickListNode的結構源碼:
typedef struct quicklist {// 頭節點指針quicklistNode *head; // 尾節點指針quicklistNode *tail; // 所有ziplist的entry的數量unsigned long count; // ziplists總數量unsigned long len;// ziplist的entry上限,默認值 -2 int fill : QL_FILL_BITS;// 首尾不壓縮的節點數量unsigned int compress : QL_COMP_BITS;// 內存重分配時的書簽數量及數組,一般用不到unsigned int bookmark_count: QL_BM_BITS;quicklistBookmark bookmarks[];
} quicklist;
typedef struct quicklistNode {// 前一個節點指針struct quicklistNode *prev;// 下一個節點指針struct quicklistNode *next;// 當前節點的ZipList指針unsigned char *zl;// 當前節點的ZipList的字節大小unsigned int sz;// 當前節點的ZipList的entry個數unsigned int count : 16; // 編碼方式:1,ZipList; 2,lzf壓縮模式unsigned int encoding : 2;// 數據容器類型(預留):1,其它;2,ZipListunsigned int container : 2;// 是否被解壓縮。1:則說明被解壓了,將來要重新壓縮unsigned int recompress : 1;unsigned int attempted_compress : 1; //測試用unsigned int extra : 10; /*預留字段*/
} quicklistNode;
QuickList的特點:
- 是一個節點為ZipList的雙端鏈表
- 節點采用ZipList,解決了傳統鏈表的內存占用問題
- 控制了ZipList大小,解決連續內存空間申請效率問題
- 中間節點可以壓縮,進一步節省了內存