美文网首页
搞懂Redis(二)-2:List数据结构

搞懂Redis(二)-2:List数据结构

作者: 高19 | 来源:发表于2022-04-14 17:05 被阅读0次

List存储结构

  1. Redis3.2前,底层是用压缩列表zipList\双向循环链表linkedList
    当list存储的数据量比较少,且同时满足下面两个条件时,list就使用zipList存储数据
  • list保存的每个元素长度小于64字节
  • 列表中数据个数少于512个
  1. Redis3.2及之后的底层实现方式:quickList
    quickList是双向链表,而且是一个基于zipList的双向链表,quickList的每个节点都是一个zipList,结合了双向链表和zipList的优点.
压缩列表(zipList)

压缩链表. 好处:节省内存空间,因为它存储的内容都是在连续的内存区域当中的.当列表对象元素不大,每个元素也不大的时候,就采用zipList存储. 但当数据量过大时,zipList就不那么好用了. 因为为了保证它存储内容在内存中的连续性,插入的复杂度为O(N),即每次插入都会重新进行realloc.
zipList的结构如下:


zipList的数据结构
  1. zlbytes: 用于记录整个压缩列表占用的内存字节数.
  2. zltail:记录列表尾节点距离压缩列表的起始地址有多少字节
  3. zllen:记录了压缩列表包含的节点数量
  4. entryX:列表包含的各个节点
  5. zlend:用于标记压缩列表的末端
为什么数据量大的时候不用zipList?

因为zipList是一段连续的内存,插入的时间复杂度O(n),而且每当插入新的元素需要realloc做内存拓展;而且如果超出zipList内存大小,还会做重新分配的内存空间,并将内容复制到新的地址. 如果数量大的话,重新分配内存和拷贝内存会消耗大量时间. 所以不适合大型字符串,也不适合存储量多的元素.

快速列表(quickList)

是zipList和linkedList的混合体,是将linkedList按段切分,每一段用zipList来紧凑存储,多个zipList之间使用双向指针链接.

为什么不直接使用linkedList?

linkedList的附加空间相对太高,prev和next指针就要占去16字节,而且每个节点都是单独分配,会加剧内存的碎片化,影响内存管理效率.
quickList结构如下

typedef struct quicklist {
    // 指向quicklist的头部
    quicklistNode *head;
    // 指向quicklist的尾部
    quicklistNode *tail;
    unsigned long count;
    unsigned int len;
    // ziplist大小限定,由list-max-ziplist-size给定
    int fill : 16;
    // 节点压缩深度设置,由list-compress-depth给定
    unsigned int compress : 16;
} quicklist;

typedef struct quicklistNode {
    // 指向上一个ziplist节点
    struct quicklistNode *prev;
    // 指向下一个ziplist节点
    struct quicklistNode *next;
    // 数据指针,如果没有被压缩,就指向ziplist结构,反之指向quicklistLZF结构
    unsigned char *zl;
    // 表示指向ziplist结构的总长度(内存占用长度)
    unsigned int sz;
    // ziplist数量
    unsigned int count : 16;     /* count of items in ziplist */
    unsigned int encoding : 2;   /* RAW==1 or LZF==2 */
    // 预留字段,存放数据的方式,1--NONE,2--ziplist
    unsigned int container : 2;  /* NONE==1 or ZIPLIST==2 */
    // 解压标记,当查看一个被压缩的数据时,需要暂时解压,标记此参数为1,之后再重新进行压缩
    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 {
    // LZF压缩后占用的字节数
    unsigned int sz; /* LZF size in bytes*/
    // 柔性数组,存放压缩后的ziplist字节数组
    char compressed[];
} quicklistLZF;

结构图如下:


quickList结构图

zipList的长度
内部默认单个zipList的长度为8K字节,超出了这个字节数,就会新起一个zipList.关于长度可以使用list-max-zipList-size决定.

压缩深度
quickList是由多个zipList组成的,同时为了进一步节省空间,Redis还会对zipList进行压缩存储,使用LZF算法压缩,可以选择压缩深度.quickList默认压缩深度为0,即不压缩. 压缩的实际深度由配置参数list-compress-depth决定.为了支持快速push/pop操作,quickList的首尾两个zipList不压缩,此时深度就是1.如果深度为2,就表示quickList的首尾第一个zipList以及首尾第二个zipList都不压缩.

相关文章

网友评论

      本文标题:搞懂Redis(二)-2:List数据结构

      本文链接:https://www.haomeiwen.com/subject/aditertx.html