redis数据结构(五) quik list
1.为什么会定义quiklist
前面已经讲过了redis中list的一个实现ziplist,由于ziplist将所有数据保存在一片连续的内存空间中所以使用ziplist不会照成内存碎片,但是也正是这样导致了ziplist不适合存储过多的数据,因为这会对重新申请内存空间和复制元素造成很大压力.为了存储大量数据,必须使用一个新的数据结构来保存.普通的linkedlist虽然也可以保存大量的数据,但是linkedlist中的每个节点中都保存了前一个节点和后一个节点的指针,而且每个节点都会单独是申请内存空间,这导致了内存使用率较低和内存碎片严重,为了兼顾内存使用率,和解决碎片问题,redis使用了一个这种的办法解决这两个问题,redis中引入了一个新的数据结构quiklist.
quiklist本质上是inkedlist和ziplist的结合体.quiklist的整体上是一个linkedlist,但是linkedlist中的每个节点都是一个ziplist.就是把linkedlist分段存储,每一段都是一个ziplist.
2.内部结构
2.1 quicklist
struct quicklist { quicklistNode* head; //头节点指针 quicklistNode* tail; //尾节点指针 long count; // 元素总数 int nodes; // ziplist 节点的个数 int compressDepth; // LZF 算法压缩深度 }
为了方便从前面和后面插入,遍历链表,quiklist中保存了头节点和尾节点的指针.
count:quiklist中元素的总个数,这样不通过遍历也可以知道整个quiklist中元素的总数
nodes:quiklist中节点的个数,即整个quiklist中总共有多少个ziplist节点.
compressDepth:压缩深度,默认为0,当为0的时候表示不压缩,当为一个大于0的整数时,表示链表两端的几个node不压缩
2.2 quiklistNode
struct quicklistNode { quicklistNode* prev; quicklistNode* next; ziplist* zl; // 指向压缩列表 int32 size; // ziplist 的字节总数 int16 count; // ziplist 中的元素数量 int2 encoding; // 存储形式 2bit,原生字节数组还是 LZF 压缩存储 }
size:当前节点占用的总字节数,包括了保存的值和头信息的大小
count:当前节点中元素的总数量
3.配置参数
list-max-ziplist-size 3
每个ziplist中节点的个数,它可以取正值,也可以取负值。
当取正值的时候,表示按照数据项个数来限定每个quicklist节点上的ziplist长度。比如,当这个参数配置成5的时候,表示每个quicklist节点的ziplist最多包含5个数据项。
当取负值的时候,表示按照占用字节数来限定每个quicklist节点上的ziplist长度。这时,它只能取-1到-5这五个值,每个值含义如下:
-5: 每个quicklist节点上的ziplist大小不能超过64 Kb。
-4: 每个quicklist节点上的ziplist大小不能超过32 Kb。
-3: 每个quicklist节点上的ziplist大小不能超过16 Kb。
-2: 每个quicklist节点上的ziplist大小不能超过8 Kb。(-2是Redis给出的默认值)
-1: 每个quicklist节点上的ziplist大小不能超过4 Kb。
list-compress-depth 2
这个参数表示一个quicklist两端不被压缩的节点个数。注:这里的节点个数是指quicklist双向链表的节点个数,而不是指ziplist里面的数据项个数。实际上,一个quicklist节点上的ziplist,如果被压缩,就是整体被压缩的。
参数list-compress-depth
的取值含义如下:
0: 是个特殊值,表示都不压缩。这是Redis的默认值。
1: 表示quicklist两端各有1个节点不压缩,中间的节点压缩。
2: 表示quicklist两端各有2个节点不压缩,中间的节点压缩。
3: 表示quicklist两端各有3个节点不压缩,中间的节点压缩。
依此类推...
4.使用
Redis 早期版本存储 list 列表数据结构使用的是压缩列表 ziplist 和普通的双向链表linkedlist,也就是元素少时用 ziplist,元素多时用 linkedlist。
考虑到链表的附加空间相对太高,prev 和 next 指针就要占去 16 个字节 (64bit 系统的指针是 8 个字节),另外每个节点的内存都是单独分配,会加剧内存的碎片化,影响内存管理效率。后续版本对列表数据结构进行了改造,使用 quicklist 代替了 ziplist 和 linkedlist。现在list只是用quiklist保存