列表(list)用來存儲多個有序的元素; 一個列表可以存儲2^32-1個元素。
Redis中的列表支持兩端插入和彈出,并可以獲得指定位置(或范圍)的元素,可以充當(dāng)數(shù)組、隊列、棧等。
1 內(nèi)部編碼
列表的內(nèi)部編碼可以是壓縮列表(ziplist)或雙端鏈表(linkedlist)
-
雙端鏈表
與雙鏈表定義一致,引入了鏈表節(jié)點,并在此基礎(chǔ)上增加頭尾節(jié)點構(gòu)建雙端鏈表
鏈表節(jié)點listNode如下定義:
/* Node, List, and Iterator are the only data structures used currently. */
/*
* 鏈表節(jié)點
*/
typedef struct listNode {
// 前驅(qū)節(jié)點
struct listNode *prev;
// 后繼節(jié)點
struct listNode *next;
// 值
void *value;
} listNode;
鏈表如下定義:
/*
* 鏈表
*/
typedef struct list {
// 表頭指針
listNode *head;
// 表尾指針
listNode *tail;
// 節(jié)點數(shù)量
unsigned long len;
// 復(fù)制函數(shù)
void *(*dup)(void *ptr);
// 釋放函數(shù)
void (*free)(void *ptr);
// 比對函數(shù)
int (*match)(void *ptr, void *key);
} list;
雙端鏈表結(jié)構(gòu)如下圖所示:
在這里插入圖片描述
通過圖中可以看出,雙端鏈表同時保存了表頭指針和表尾指針,并且每個節(jié)點都有指向前和指向后的指針;鏈表中保存了列表的長度;dup、free和match為節(jié)點值設(shè)置類型特定函數(shù),所以鏈表可以用于保存各種不同類型的值。而鏈表中每個節(jié)點指向的是type為字符串的redisObject。
-
壓縮列表
壓縮列表是Redis為了節(jié)約內(nèi)存而開發(fā)的,是由一系列特殊編碼的連續(xù)內(nèi)存塊(而不是像雙端鏈表一樣每個節(jié)點是指針)組成的順序型數(shù)據(jù)結(jié)構(gòu)。
壓縮列表的結(jié)構(gòu) -
zlbytes記錄整個壓縮列表占用的內(nèi)存字節(jié)數(shù),在對壓縮列表進(jìn)行內(nèi)存重分配或計算 -
zlend的位置時使用 -
zltail記錄壓縮列表尾節(jié)點距離壓縮列表的起始地址有多少字節(jié),通過這個偏移量,可以直接確定尾節(jié)點的位置 -
zllen記錄壓縮列表包含的節(jié)點數(shù)量 -
entryX表示各種節(jié)點,數(shù)量和長度不一定 -
zlend用于標(biāo)記壓縮列表的末端。
如圖,如果有一個指針p指向該壓縮列表,則尾巴節(jié)點的長度就是指針加上偏移量179(十六進(jìn)制0xb3=16*11+3=179),列表的長度zllen為5,表示壓縮列表包含5個節(jié)點。zlbytes為0xd2表示壓縮列表的總長為210字節(jié)。
在這里插入圖片描述
由上可知,每個壓縮列表的節(jié)點可以保存一個字節(jié)數(shù)組或者一個整數(shù)值,那么每個節(jié)點肯定也有自己的結(jié)構(gòu)。
與雙端鏈表相比,壓縮列表可以節(jié)省內(nèi)存空間,但是進(jìn)行修改或增刪操作時,復(fù)雜度較高
因此當(dāng)節(jié)點數(shù)量較少時,可以使用壓縮列表;但是節(jié)點數(shù)量多時,還是使用雙端鏈表劃算
壓縮列表不僅用于實現(xiàn)列表,也用于實現(xiàn)哈希、有序列表;使用非常廣泛。