之前的文章我曾总结到了Redis数据结构一链表和Redis数据结构一压缩列表这两种数据结构,他们是Redis List(列表)对象的底层实现方式。
但是考虑到艇表的附加空间相对太高,prev和next 指针就要占去 16个字节(64bit 系统的指针是8个字节),另外每个节点的内存都是单独分配,会加剧内存的碎片化,影响内存管理效率。
因此Redis3.2版本开始对列表数据结构进行了改造,使用quicklist代替了 ziplist和 linkedlist.
如图:

一、基本结构
quicklist 实际上是ziplist和linkedList的混合体,它将linkedList 按段切分,每一段使用ziplist 来紧凑存储,多个zipList之间使用双向指针串接起来。
Redis3.2之前: 列表对象的底层数据结构(linkedlist 与 ziplist)为:

Redis3.2之后,列表对象的底层数据结构(quicklist)为:

typedef struct quicklistNode {
struct quicklistNode *prev; //上一个node节点
struct quicklistNode *next; //下一个node
unsigned char *zl; //保存的数据 压缩前ziplist 压缩后压缩的数据
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;

typedef struct quicklistLZF {
unsigned int sz; /* LZF size in bytes*/
char compressed[];
} quicklistLZF;

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 : QL_FILL_BITS; /* fill factor for individual nodes */
unsigned int compress : QL_COMP_BITS; /* depth of end nodes not to compress;0=off */
unsigned int bookmark_count: QL_BM_BITS;
quicklistBookmark bookmarks[];
} quicklist;

二、常用操作
2.1插入
quicklist可以选择在头部或者尾部进行插入( quicklistPushHead和quicklistrushTail ),而不管是在头部还是尾部插入数据,都包含两种情况:
- ·如果头节点(或尾节点) 上ziplist大小没有超过限制(即_quicklistModeAlLowEInsert 返回1),那么新数据被直接插入到ziplist中(调明ziplisteush ) 。
- ·如果头节点(或尾节点)上ziplist太大了,那么新创建一个quicklistlode节点(对应地也会新创建一个ziplist),然后把这个新创建的节点插入到quicklist双向链表中。

也可以从任意指定的位置插入。
quicklistInsertAfter和quicklistInsertBefore就是分别在指定位置后面和前面插入数据项。
这种在任意指定位置插入数据的操作,要比在头部和尾部的进行插入要复杂一些。
- 当插入位置所在的ziplist大小没有超过限制时,直接插入到ziplist中就好了;
- 当插入位置所在的ziplist大小超过了限制,但插入的位置位于ziplist两端,并且相邻的quicklist链表节点的ziplist大小没有超过限制,那么就转而插入到相邻的那个quicklist链表节点的ziplist中;
- 当插入位置所在的ziplist大小超过了限制,但插入的位置位于ziplist两端,并且相邻的Squicklist链表节点的ziplist大小也超过限制,这时需要新创建一个quicklist链表节点插入。
- 对于插入位置所在的ziplist大小超过了限制的其它情况(主要对应于在ziplist中间插入数据的情况),则需要把当前ziplist分裂为两个节点,然后再其中一个节点上插入数据
2.2查找
list的查找操作主要是对index的我们的quicklist的节点是由一个一个的ziplist构成的每个ziplist都有大小。所以我们就只需要先根据我们每个node的个数,从而找到对应的ziplist,调用ziplist的index就能成功找到。
2.3 删除
区间元素删除的函数是 quicklistDelRangequicklist 在区间删除时,会先找到start 所在的 quicklistlode,计算删除的元素是否小于要删除的count,如果不满足删除的个数,则会移动至下一个quicklistNode 继续删除,依次循环直到删除完成为止。
quicklistDelRange 函数的返回值为int类型,当返回1时表示成功的删除了指定区间的元素,返回0时表示没有删除任何元素。
小结
Redis quicklist是Redis 3.2版本以后针对链表和压缩列表进行改造的一种数据结构,是ziplist和1inkedList的混合体,相对于键表它压缩了内存。进一步的提高了效率。
Redis3.2版本开始使用quicklist替代linkedlist和ziplist,作为列表对象的底层数据结构。quicklist是ziplist和双向链表的结合,通过双向指针连接多个ziplist,降低了空间开销并减少内存碎片。插入、查找和删除操作在quicklist中高效执行,优化了列表操作的性能。
1108

被折叠的 条评论
为什么被折叠?



