深入了解Redis中List底层结构

164 阅读5分钟
  • 存储类型与操作命令
类型(Type key)存储编码(OBJECT encoding key)实例命令
listquicklist是Redis 3.2中新引入的数据结构,能够在时间效率和空间 效率间实现较好的折中。Redis中对quciklist的注释为A doubly linked list of ziplists。顾名思义,quicklist是一个双向链表,链表中的每个节点是 一个ziplist结构。quicklist可以看成是用双向链表将若干小型的ziplist连 接到一起组成的一种数据结构。当ziplist节点个数过多,quicklist退化 为双向链表,一个极端的情况就是每个ziplist节点只包含一个entry,即 只有一个元素。当ziplist元素个数过少时,quicklist可退化为ziplist,一 种极端的情况就是quicklist中只有一个ziplist节点lpush queue a b c d e

存储原理

  • quicklist
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 : 16;/* fill factor for individual nodes */
    unsigned int compress : 16; /* depth of end nodes not to compress;0=off */
} quicklist;
属性说明
head指向quicklist的首节点
tail指向quicklist的尾节点
countcount为quicklist中元素entry的总数和
len为quicklist Node(节点)个数
fill当fill为正数时,表明每个(单个) ziplist最多含有的数据项数,最大值为 2的15次幂 当fill为负数时: -1: 每个quicklistNode节点的ziplist字节大小不能超过4kb。(建议) -2 :每个quicklistNode节点的ziplist字节大小不能超过8kb。(默认配置) -3 :每个quicklistNode节点的ziplist字节大小不能超过16kb。(一般不建议) -4 :每个quicklistNode节点的ziplist字节大小不能超过32kb。(不建议) -5 :每个quicklistNode节点的ziplist字节大小不能超过64kb。(正常工作量不建议) fill取负数时,必须大于等于-5。我们可以通 过Redis修改参数list-max-ziplist-size配置节点所占内存大小。实际上每 个ziplist节点所占的内存会在该值上下浮动
compress考虑quicklistNode节点个 数较多时,我们经常访问的是两端的数据,为了进一步节省空间,Redis允许对中间的quicklistNode节点进行压缩,通过修改参数list- compress-depth进行配置 0 表示不压缩。(默认) 1 表示quicklist列表的两端各有1个节点不压缩,中间的节点压缩。 2 表示quicklist列表的两端各有2个节点不压缩,中间的节点压缩。 3 表示quicklist列表的两端各有3个节点不压缩,中间的节点压缩。 以此类推,最大为 2的16次幂
  • quicklistNode
typedef struct quicklistNode {
    struct quicklistNode *prev;
    struct quicklistNode *next;
    unsigned char *zl;
    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;
属性说明
prevprev指向该节点的前节点
nextnext指向该节点的后节点
zlzl指向该节点对应的ziplist结构 不设置压缩数据参数recompress时指向一个ziplist结构 设置压缩数据参数recompress指向quicklistLZF结构
szsz代表整个ziplist结构的大小
countziplist中包的节点数,占16 bits长度
encoding表示是否采用了LZF压缩算法压缩quicklist节点,1表示压缩过,2表示没压缩,占2 bits长度
container表示一个quicklistNode节点是否采用ziplist结构保存数据,2表示压缩了,1表示没压缩,默认是2,占2bits长度
recompressrecompress代表这个节点之前是否是压缩节点,若是,则在使用压缩节点前先进行解压缩,使用后需要重新压缩,如果recompress为1,则等待被再次压缩
attempted_compress测试时使用
extra额外扩展位(预留),占10bits长度
  • quicklistLZF
/*当指定使用lzf压缩算法压缩ziplist的entry节点时,quicklistNode结构的zl成员指向quicklistLZF结构*/
typedef struct quicklistLZF {
    //表示被LZF算法压缩后的ziplist的大小
    unsigned int sz; /* LZF size in bytes*/
​
    //保存压缩后的ziplist的数组,柔性数组
    char compressed[];
} quicklistLZF;
  • quicklistEntry
//管理quicklist中quicklistNode节点中ziplist信息的结构
typedef struct quicklistEntry {
    const quicklist *quicklist;   //指向所属的quicklist的指针
    quicklistNode *node;          //指向所属的quicklistNode节点的指针
    unsigned char *zi;            //指向当前ziplist结构的指针
    unsigned char *value;         //指向当前ziplist结构的字符串vlaue成员
    long long longval;            //指向当前ziplist结构的整数value成员
    unsigned int sz;              //保存当前ziplist结构的字节数大小
    int offset;                   //保存相对ziplist的偏移量
} quicklistEntry;
  • quicklistIter
//quicklist的迭代器结构
typedef struct quicklistIter {
    const quicklist *quicklist;   //指向所属的quicklist的指针
    quicklistNode *current;       //指向当前迭代的quicklist节点的指针
    unsigned char *zi;            //指向当前quicklist节点中迭代的ziplist
    long offset;                  //当前ziplist结构中的偏移量      /* offset in current ziplist */
    int direction;                //迭代方向
} quicklistIter;
  • zlentry
typedef struct zlentry {
    unsigned int prevrawlensize; /*存储上一个链表节点的长度数值所需的字节*/
    unsigned int prevrawlen;  /*上一个链表节点占用的长度*/   
    unsigned int lensize;  /*存储当前链表节点长度数值所需要的字节数*/      
    unsigned int len;    /*当前链表节点占用的长度*/        
    unsigned int headersize;    /*当前链表节点的头部大小(prevrawlensize + lensize),即非数据域的大小*/ 
    unsigned char encoding;  /*编码方式*/    
    unsigned char *p;    /*压缩链表以字符串的形式保存,该指针指向当前节点起始位置*/       
} zlentry;
​
  • 结构示意图

quicklist源码架构图.png

应用场景

  • 消息队列
  • 文章l列表
  • 评论列表
  • 活动列表

\