quicklist.c - A doubly linked list of ziplists
通过上面这句话可以了解到,快速列表是由双向压缩链表实现的。
先来看看quicklist的结构:
typedef struct quicklist {
quicklistNode *head; /* 指向头结点 */
quicklistNode *tail; /* 指向尾结点 */
unsigned long count; /* entry个数 */
unsigned long len; /* 节点个数 */
int fill : 16; /* 单个节点的充填因子 */
/* compress=-1时,说明不能压缩;否则就是在quicklist的末尾没有压缩的节点数量;
* =0时,表示不压缩
*/
unsigned int compress : 16;
} quicklist;
再看看quicklistNode的结构:
typedef struct quicklistNode {
struct quicklistNode *prev; /* 指向当前节点的前一个节点 */
struct quicklistNode *next; /* 指向当前节点的后一个节点 */
unsigned char *zl; /* 指针,指向一个ziplist */
unsigned int sz; /* ziplist的大小 */
unsigned int count : 16; /* 计数 */
unsigned int encoding : 2; /* 编码;RAW==1,LZF==2 */
unsigned int container : 2; /* 容器;NONE==1,ZIPLIST==2 */
/* 这个节点是否被压缩过? 如果节点在使用时,是临时解压,则为true*/
unsigned int recompress : 1;
unsigned int attempted_compress : 1; /* 节点太小则不能压缩 */
unsigned int extra : 10; /* 冗余字段 */
} quicklistNode;
那么在什么情况下会进行压缩呢?来看看压缩方法:
/* 只会压缩没有被压缩的节点 */
#define quicklistCompressNode(_node)
do {
if ((_node) && (_node)->encoding == QUICKLIST_NODE_ENCODING_RAW) {
__quicklistCompressNode((_node));
}
} while (0)
这里压缩的具体实现在_quicklistCompressNode方法中:
/* 压缩成功返回1,压缩失败或者列表太小了不用压缩则返回0 */
REDIS_STATIC int __quicklistCompressNode(quicklistNode *node) {
#ifdef REDIS_TEST
node->attempted_compress = 1;
#endif
/* 小于48字节,不需要用到压缩 */
if (node->sz < MIN_COMPRESS_BYTES)
return 0;
/* 否则分配空间,这是数据结构是quicklistLZF */
quicklistLZF *lzf = zmalloc(sizeof(*lzf) + node->sz);
/* 压缩失败或者不用压缩,那么需要释放lzf的空间,并且返回0 */
if (((lzf->sz = lzf_compress(node->zl, node->sz, lzf->compressed,
node->sz)) == 0) ||
lzf->sz + MIN_COMPRESS_IMPROVE >= node->sz) {
zfree(lzf);
return 0;
}
/* 以下操作就是压缩节点,将数据结构改成quicklistLZF */
lzf = zrealloc(lzf, sizeof(*lzf) + lzf->sz);
zfree(node->zl);
node->zl = (unsigned char *)lzf;
node->encoding = QUICKLIST_NODE_ENCODING_LZF;
node->recompress = 0;
return 1;
}
可见,在压缩之后,数据结构不再是quicklist而是quicklistLZF,它的结构如下:
/* 没有被压缩的长度存放在quicklistNode节点的sz属性中
* 当节点被压缩的时候,这个节点的数据结构就是quicklistLZF */
typedef struct quicklistLZF {
unsigned int sz; /* compressed数组长度 */
char compressed[];
} quicklistLZF;