Redis底层数据结构 ------> quicklist

Redis3.2版本开始使用quicklist替代linkedlist和ziplist,作为列表对象的底层数据结构。quicklist是ziplist和双向链表的结合,通过双向指针连接多个ziplist,降低了空间开销并减少内存碎片。插入、查找和删除操作在quicklist中高效执行,优化了列表操作的性能。

之前的文章我曾总结到了Redis数据结构一链表和Redis数据结构一压缩列表这两种数据结构,他们是Redis List(列表)对象的底层实现方式

但是考虑到艇表的附加空间相对太高,prev和next 指针就要占去 16个字节(64bit 系统的指针是8个字节),另外每个节点的内存都是单独分配,会加剧内存的碎片化,影响内存管理效率。

因此Redis3.2版本开始对列表数据结构进行了改造,使用quicklist代替了 ziplist和 linkedlist.

 

如图:

一、基本结构


quicklist 实际上是ziplistlinkedList的混合体,它将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双向链表中。

 

也可以从任意指定的位置插入。

quicklistInsertAfterquicklistInsertBefore就是分别在指定位置后面和前面插入数据项。

这种在任意指定位置插入数据的操作,要比在头部和尾部的进行插入要复杂一些。

  • 当插入位置所在的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的混合体,相对于键表它压缩了内存。进一步的提高了效率。
 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值