list取值_闲扯Redis五 : List数据类型底层之quicklist

9d11b03e5269c12559682024547a6624.png

前言

     Redis提供了5种数据类型:String(字符串)、Hash(哈希)、List(列表)、Set(集合)、Zset(有序集合),理解每种数据类型的特点对于redis的开发和运维非常重要。

ef34a36c741a5b1f31ab1ff1c900f223.png

Redis中的list是我们经常使用到的一种数据类型,根据使用方式的不同,可以应用到很多场景中。

底层解析

上节说道,在3.0版本的redis中,List类型有两种实现方式:

1、使用压缩列表(ziplist)实现的列表对象。

2、使用双端链表( linkedlist )实现的列表对象。

在3.2版本后新增了quicklist 数据结构实现了list,现在就来分析下quicklist的结构

先来看看Redis官方对quicklist的描述:

A doubly linked list of ziplists

A generic doubly linked quicklist implementation

可见 quicklist 是一个双向链表,并且是一个ziplist的双向链表,也就是说quicklist的每个节点都是一个ziplist。而通过前面的文章咱们可以知道,ziplist本身也是一个能维持数据项先后顺序的列表,而且数据项保存在一个连续的内存块中。那是不是意味着quicklist结合了压缩列表和双端链表的特点呢!

quicklist结构定义:

/*  * quicklist */typedef struct quicklist {    //头结点    quicklistNode *head;     //尾节点    quicklistNode *tail;     //所有ziplist中entry数量    unsigned long count;     //quicklistNodes节点数量    unsigned int len;       //ziplist中entry能保存的数量,由list-max-ziplist-size配置项控制     int fill : 16;           //压缩深度,由list-compress-depth配置项控制    unsigned int compress : 16; } quicklist;

注释:

fill ://ziplist中entry能保存的数量,由list-max-ziplist-size配置项控制

        表示了单个节点(quicklistNode)的负载比例(fill factor),负数限制 quicklistNode 中的 ziplist 的字节长度, 正数限制 quicklistNode 中的 ziplist 的最大长度。

-5: 最大存储空间: 64 Kb -4: 最大存储空间: 32 Kb -3: 最大存储空间: 16 Kb -2: 最大存储空间: 8 Kb -1: 最大存储空间: 4 Kb 对于正整数则表示最多能存储到你设置的那个值, 当前的节点就装满了通常在 -2 (8 Kb size) 或 -1 (4 Kb size) 时, 性能表现最好

compress ://压缩深度,由list-compress-depth配置项控制

                表示 quicklist 中的节点 quicklistNode, 除开最两端的 compress 个节点之后, 中间的节点都会被压缩(LZF压缩算法)。

quicklistNode结构定义:

typedef struct quicklistNode {    //前节点指针    struct quicklistNode *prev;     //后节点指针    struct quicklistNode *next;     //数据指针。当前节点的数据没有压缩,那么它指向一个ziplist结构;否则,它指向一个quicklistLZF结构。    unsigned char *zl;    //zl指向的ziplist实际占用内存大小。需要注意的是:如果ziplist被压缩了,那么这个sz的值仍然是压缩前的ziplist大小    unsigned int sz;      //ziplist里面包含的数据项个数    unsigned int count : 16;       //ziplist是否压缩。取值:1--ziplist,2--quicklistLZF     unsigned int encoding : 2;     //存储类型,目前使用固定值2 表示使用ziplist存储    unsigned int container : 2;     //当我们使用类似lindex这样的命令查看了某一项本来压缩的数据时,需要把数据暂时解压,这时就设置recompress=1做一个标记,等有机会再把数据重新压缩    unsigned int recompress : 1;    unsigned int attempted_compress : 1; /* node can't compress; too small */    unsigned int extra : 10; /* more bits to steal for future usage */} quicklistNode;

quicklistLZF结构定义

typedef struct quicklistLZF {    unsigned int sz;  //压缩后的ziplist大小    char compressed[];//柔性数组,存放压缩后的ziplist字节数组} quicklistLZF;

根据上述结构体定义,咱们可以绘制一下quicklist的结构:

bc091f87cda26124739cba4c3ecd3fcb.png

图片来源:https://segmentfault.com/a/1190000015476108

总结

双端链表:

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

2.双端链表每个节点上除了要保存数据之外,还要额外保存两个指针;

3.双端链表的各个节点是单独的内存块,地址不连续,节点多了容易产生内存碎片;

压缩列表:

1.ziplist由于是一整块连续内存,所以存储效率很高;

2.ziplist不利于修改操作,每次数据变动都会引发一次内存的realloc;

3.当ziplist长度很长的时候,一次realloc可能会导致大批量的数据拷贝,进一步降低性能;

quicklist:

1.空间效率和时间效率的折中;

2.结合了双端链表和压缩列表的优点;

9d495aa106841002a7db547af468f34f.png

-END-

f0277a5019cc2cafa2272b51d7b3f3d6.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值