Redis 提供了5種數據類型:String(字符串)、Hash(哈希)、List(列表)、Set(集合)、Zset(有序集合),理解每種數據類型的特色對於redis的開發和運維很是重要。html
原文解析java
Redis 中的 list 是咱們常用到的一種數據類型,根據使用方式的不一樣,能夠應用到不少場景中。node
上節**《閒扯Redis四》List數據類型底層編碼轉換** 說道,在 3.0 版本的 Redis 中,List 類型有兩種實現方式:redis
一、使用壓縮列表(ziplist)實現的列表對象。算法
二、使用雙端鏈表(linkedlist)實現的列表對象。數組
在 3.2 版本後新增了 quicklist 數據結構實現了 list,如今就來分析下 quicklist 的結構數據結構
先來看看 Redis 官方對 quicklist 的描述:運維
A doubly linked list of ziplists
A generic doubly linked quicklist implementation
複製代碼
可見 quicklist 是一個雙向鏈表,而且是一個 ziplist 的雙向鏈表,也就是說 quicklist 的每一個節點都是一個 ziplist。而經過前面的文章我們能夠知道,ziplist 自己也是一個能維持數據項前後順序的列表,並且數據項保存在一個連續的內存塊中。那是否是意味着 quicklist 結合了壓縮列表和雙端鏈表的特色呢!性能
/* * quicklist */
typedef struct quicklist {
//頭結點
quicklistNode *head;
//尾節點
quicklistNode *tail;
//全部ziplist中entry數量
unsigned long count;
//quicklistNodes節點數量
unsigned int len;
//ziplist中entry能保存的數量,由list-max-ziplist-size配置項控制
int fill : 16;
//壓縮深度,由list-compress-depth配置項控制
unsigned int compress : 16;
} quicklist;
複製代碼
註釋:ui
fill :ziplist 中 entry 能保存的數量,由 list-max-ziplist-size 配置項控制
表示了單個節點(quicklistNode)的負載比例(fill factor),負數限制 quicklistNode 中的 ziplist 的字節長度,
正數限制 quicklistNode 中的 ziplist 的最大長度。
複製代碼
-5: 最大存儲空間: 64 Kb <-- 一般狀況下不要設置這個值
-4: 最大存儲空間: 32 Kb <-- 很是不推薦
-3: 最大存儲空間: 16 Kb <-- 不推薦
-2: 最大存儲空間: 8 Kb <-- 推薦
-1: 最大存儲空間: 4 Kb <-- 推薦
對於正整數則表示最多能存儲到你設置的那個值, 當前的節點就裝滿了
一般在 -2 (8 Kb size) 或 -1 (4 Kb size) 時, 性能表現最好
複製代碼
compress :壓縮深度,由 list-compress-depth 配置項控制
表示 quicklist 中的節點 quicklistNode, 除開最兩端的 compress 個節點以後, 中間的節點都會被壓縮(LZF壓縮算法)。
複製代碼
typedef struct quicklistNode {
//前節點指針
struct quicklistNode *prev;
//後節點指針
struct quicklistNode *next;
//數據指針。當前節點的數據沒有壓縮,那麼它指向一個ziplist結構;不然,它指向一個quicklistLZF結構。
unsigned char *zl;
//zl指向的ziplist實際佔用內存大小。須要注意的是:若是ziplist被壓縮了,那麼這個sz的值仍然是壓縮前的ziplist大小
unsigned int sz;
//ziplist裏面包含的數據項個數
unsigned int count : 16;
//ziplist是否壓縮。取值:1--ziplist,2--quicklistLZF
unsigned int encoding : 2;
//存儲類型,目前使用固定值2 表示使用ziplist存儲
unsigned int container : 2;
//當咱們使用相似lindex這樣的命令查看了某一項原本壓縮的數據時,須要把數據暫時解壓,這時就設置recompress=1作一個標記,等有機會再把數據從新壓縮
unsigned int recompress : 1;
unsigned int attempted_compress : 1; /* node can't compress; too small */
unsigned int extra : 10; /* more bits to steal for future usage */
} quicklistNode;
複製代碼
typedef struct quicklistLZF {
unsigned int sz; //壓縮後的ziplist大小
char compressed[];//柔性數組,存放壓縮後的ziplist字節數組
} quicklistLZF;
複製代碼
根據上述結構體定義,我們能夠繪製一下 quicklist 的結構:
1.雙端鏈表便於在表的兩端進行 push 和 pop 操做,可是它的內存開銷比較大;
2.雙端鏈表每一個節點上除了要保存數據以外,還要額外保存兩個指針;
3.雙端鏈表的各個節點是單獨的內存塊,地址不連續,節點多了容易產生內存碎片;
1.ziplist 因爲是一整塊連續內存,因此存儲效率很高;
2.ziplist 不利於修改操做,每次數據變更都會引起一次內存的 realloc;
3.當 ziplist 長度很長的時候,一次 realloc 可能會致使大批量的數據拷貝,進一步下降性能;
1.空間效率和時間效率的折中;
2.結合了雙端鏈表和壓縮列表的優勢;