一. 简介

  在redis源码中可以看到当前主要使用的并不是压缩链表而是快速链表,快速链表由节点、快速链表,迭代器组成。为什么这样设计呢?总结起来,大概又是一个空间和时间的折中:

  双向链表便于在表的两端进行push和pop操作,但是它的内存开销比较大。

  • 首先,它在每个节点上除了要保存数据之外,还要额外保存两个指针;
  • 其次,双向链表的各个节点是单独的内存块,地址不连续,节点多了容易产生内存碎片。

   ziplist由于是一整块连续内存,所以存储效率很高。但是,它不利于修改操作,每次数据变动都会引发一次内存的realloc。特别是当ziplist长度很长的时候,一次realloc可能会导致大批量的数据拷贝,进一步降低性能。

  考虑到二者间的折中选择,就创造出了快速链表这种数据结构:快速链表从宏观来看是一系列节点组成的双向链表,而每个节点的数据则是由一个压缩链表存储。因此可以兼具二者的优点,规避二者的缺点,算是一个比较好的选择。

二. 结构体源码分析

  下面通过源码和注释来分析快速链表的结构

/* 快速链表节点:32字节,底层其实是压缩链表
 * count:压缩链表中的节点数
 * encoding:编码类型ziplist或者lzf
 * container:是否使用了ziplist
 * recompress:是否开启压缩/解压
 * quicklistNode is a 32 byte struct describing a ziplist for a quicklist.
 * We use bit fields keep the quicklistNode at 32 bytes.
 * count: 16 bits, max 65536 (max zl bytes is 65k, so max count actually < 32k).
 * encoding: 2 bits, RAW=1, LZF=2.
 * container: 2 bits, NONE=1, ZIPLIST=2.
 * recompress: 1 bit, bool, true if node is temporarry decompressed for usage.
 * attempted_compress: 1 bit, boolean, used for verifying during testing.
 * extra: 12 bits, free for future use; pads out the remainder of 32 bits 
 */
typedef struct quicklistNode {
    struct quicklistNode *prev;
    struct quicklistNode *next;
    unsigned char *zl;
    unsigned int sz;             /* ziplist size in bytes */
    unsigned int count : 16;     /* count of items in ziplist */
    unsigned int encoding : 2;   /* RAW==1 or LZF==2 */
    unsigned int container : 2;  /* NONE==1 or ZIPLIST==2 */
    unsigned int recompress : 1; /* was this node previous compressed? */
    unsigned int attempted_compress : 1; /* node can't compress; too small */
    unsigned int extra : 10; /* more bits to steal for future usage */
} quicklistNode;

/* 采用lzf压缩的数据存储结构体
 * quicklistLZF is a 4+N byte struct holding 'sz' followed by 'compressed'.
 * 'sz' is byte length of 'compressed' field.
 * 'compressed' is LZF data with total (compressed) length 'sz'
 * NOTE: uncompressed length is stored in quicklistNode->sz.
 * When quicklistNode->zl is compressed, node->zl points to a quicklistLZF 
 */
typedef struct quicklistLZF {
    unsigned int sz; /* LZF size in bytes*/
    char compressed[];
} quicklistLZF;

/* 快速链表
 * quicklist is a 40 byte struct (on 64-bit systems) describing a quicklist.
 * 'count' is the number of total entries.
 * 'len' is the number of quicklist nodes.
 * 'compress' is: -1 if compression disabled, otherwise it's the number
 *                of quicklistNodes to leave uncompressed at ends of quicklist.
 * 'fill' is the user-requested (or default) fill factor. 
 */
typedef struct quicklist {
    quicklistNode *head;
    quicklistNode *tail;
    unsigned long count;        /* 压缩链表节点总数 total count of all entries in all ziplists */
    unsigned long len;          /* 快速链表节点总数 number of quicklistNodes */
    int fill : 16;              /* ziplist大小设置,存放list-max-ziplist-size参数的值
    						     * fill factor for individual nodes */
    unsigned int compress : 16; /* 不需要压缩的尾结点数 depth of end nodes not to compress;0=off */
} quicklist;


/* 快速链表迭代器,包括链表指针,当前节点,偏移量,方向,当前quicklist节点中迭代的ziplist*/
typedef struct quicklistIter {
    const quicklist *quicklist;
    quicklistNode *current;
    unsigned char *zi;
    long offset; /* offset in current ziplist */
    int direction;
} quicklistIter;

/* 快速链表表项,便于插入和遍历 */
typedef struct quicklistEntry {
    const quicklist *quicklist;
    quicklistNode *node;
    unsigned char *zi;
    unsigned char *value;
    long long longval;
    unsigned int sz;
    int offset;
} quicklistEntry;