太长不看版
- 快速列表是一个元素为压缩列表的双向链表。
- 快速列表是列表对象list的底层实现之一。
- 快速列表是在Redis3.2版本引入的。
- 快速列表节点中压缩列表的最大字节长度(配置项为负数时)或最多元素个数(配置项为正数时)由配置项 list-max-ziplist-size 决定,默认约束为最大长度8Kb。
- 快速列表提供了选项可以使用LZF压缩算法对中间的节点中的ziplist进行压缩,列表两边多少节点不被压缩由配置项 list-compress-depth 决定,默认对所有节点不进行压缩。
本篇解析基于redis 5.0.0版本,本篇涉及源码文件为quicklist.c, quicklist.h, redic.conf。
什么是快速列表
快速列表是一个元素为压缩列表的双向链表。
qulicklist是列表对象(list)的底层实现之一,是在Redis3.2中为了兼顾空间效率与时间效率而引入的。
压缩列表非常的节省空间,但是当节点过多或者过大之后,因为修改元素时需要重新分配存储空间导致效率会非常低下,所以只在节点较少且较小时使用。
压缩列表相关戳这里了解
双向链表修改节点效率比较高(复杂度O(1)),但是数据结构元数据占用的空间相对比较大(每个节点有两个指针占用16个字节)。
快速列表在时间效率和空间效率之间做了折中,由双向链表将若干个压缩列表连接在一起组成,相比于双向链表更加节省空间,相比于压缩列表操作更加的高效。
快速列表的结构定义
快速列表节点定义
typedef struct quicklistNode {
// 向前指针
struct quicklistNode *prev;
// 向后指针
struct quicklistNode *next;
// ziplist或者是被压缩之后的ziplist(quicklistLZF)
unsigned char *zl;
// ziplist的字节长度,不受压缩影响
unsigned int sz;
// ziplist中包含的元素个数
unsigned int count : 16; /* count of items in ziplist */
// ziplist编码标示,1为原始数据,2为被压缩后数据
unsigned int encoding : 2; /* RAW==1 or LZF==2 */
// 数据存储方式(当前只有ziplist)
unsigned int container : 2; /* NONE==1 or ZIPLIST==2 */
// 需要再次被压缩标记,压缩节点被解压缩读取后会置为1
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;
typedef struct quicklistLZF {
// ziplist被压缩之后的字节长度
unsigned int sz; /* LZF size in bytes*/
// ziplist被压缩后的内容
char compressed[];
} quicklistLZF;
typedef struct quicklist {
// 头节点
quicklistNode *head;
// 尾节点
quicklistNode *tail;
// 快速列表中所有元素的个数
unsigned long count;
// 快速列表节点(quicklistNode)个数
unsigned long len;
// 节点中ziplist长度的大小
int fill : 16;
// 头部开始或尾部开始 分别有几个节点不被压缩
unsigned int compress : 16;
} quicklist;
因为快速列表的常见的应用场景大多是访问两边的节点(例如:lpush, lpop, rpush, rpop等),为了进一步的节省空间,快速列表提供了选项可以使用LZF压缩算法对中间的节点中的ziplist进行压缩,节点压缩后原本指向ziplist的sz指针元素指向quicklistLZF结构体。
快速列表两边分别有几个节点不被压缩有配置项 list-compress-depth 决定,存储在qulicklist的compress元素中,默认为0(不进行节点压缩)。
快速列表节点中压缩列表的最大字节长度或最多元素个数由配置项 list-max-ziplist-size 决定,存储在qulicklist的fill元素中。配置项为正数时表示节点中压缩列表最多元素个数,为负数时表示节点中压缩列表最大字节长度,默认约束为快速列表节点中压缩列表最大长度为8Kb。
值 | 节点ziplist最大长度 |
---|---|
-5 | 64Kb |
-4 | 32Kb |
-3 | 16Kb |
-2 | 8Kb |
-1 | 4Kb |
在快速列表的结构体定义中,使用了结构体位域(即 unsigned int count : 16; 这种写法),其实就是为了节省空间某些元素只使用几个bit位长度的空间。详细的解释戳这里。
快速列表相关操作
push操作
#ifdef __GNUC__
# d