加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

Redis系列(六):数据结构QuickList(快速列表)源码解析

发布时间:2020-12-16 04:38:05 所属栏目:安全 来源:网络整理
导读:1.介绍 Redis在3.2版本之前List的底层编码是ZipList和LinkedList实现的 在3.2版本之后,重新引入了QuickList的数据结构,列表的底层都是QuickList实现 当List对象中元素的长度比较小或者数量比较少的时候,采用ZipList来存储 当List对象中元素的长度比较大或

1.介绍

Redis在3.2版本之前List的底层编码是ZipList和LinkedList实现的

在3.2版本之后,重新引入了QuickList的数据结构,列表的底层都是QuickList实现

当List对象中元素的长度比较小或者数量比较少的时候,采用ZipList来存储

当List对象中元素的长度比较大或者数量比较多的时候,采用LinkList来存储

这两种存储方式的优缺点

LinkedList便于在表的两端进行Push和Pop操作,在插入节点复杂度很低,但是它的内存开销很大,首先,它在每个节点上除了要保存数据之外,还要额外保存两个指针。

其次;双向链表的各个节点时单独的内存块,地址不连续,节点多了容易产生内存碎片

ZipList存储在一段连续的内存上,所以存储效率很高,但是它不利于修改操作,插入和删除操作需要频繁的申请释放内存。

特别是当ZipList长度很长的时候,一次Realloc可能导致大批量的数据拷贝

QuickList可以看作一个双向列表,但是列表的每个节点都是一个ziplist,

其实就是linkedlist和ziplist的结合。quicklist中的每个节点ziplist都能够存储多个数据元素。

2.示意图

?

?

?3.源码实现

typedef struct quicklist {
    quicklistNode *head;        // 指向quicklist的头部
    quicklistNode *tail;         指向quicklist的尾部
    unsigned long count;         列表中所有数据项的个数总和
    unsigned int len;            quicklist节点的个数,即ziplist的个数
    int fill : 16;               ziplist大小限定,由list-max-ziplist-size给定
    unsigned int compress : 16;  节点压缩深度设置,由list-compress-depth给定
} quicklist;

count用来统计所有数据项的个数总和,len用来统计quicklist的节点个数, 因为每个节点ziplist都能存储多个数据项,所以有了这两个统计值。

typedef  quicklistNode {
    struct quicklistNode *prev;   指向上一个ziplist节点
    struct quicklistNode *next;   指向下一个ziplist节点
    unsigned char *zl;            数据指针,如果没有被压缩,就指向ziplist结构,反之指向quicklistLZF结构 
    unsigned int sz;              表示指向ziplist结构的总长度(内存占用长度)
    unsigned int count : 16;      表示ziplist中的数据项个数
    unsigned int encoding : 2;    编码方式,1--ziplist,2--quicklistLZF
    unsigned int container : 2;   预留字段,存放数据的方式,1--NONE,2--ziplist
    unsigned int recompress : 1;  解压标记,当查看一个被压缩的数据时,需要暂时解压,标记此参数为1,之后再重新进行压缩
    unsigned int attempted_compress :  测试相关
    unsigned int extra : 10;  扩展字段,暂时没用
} quicklistNode;

QuickList的迭代器结构,指向迭代器节点元素

 quicklist的迭代器结构
typedef  quicklistIter {
    const quicklist *quicklist;   指向所在quicklist的指针
    quicklistNode *current;   指向当前节点的指针
    unsigned char *zi;   指向当前节点的ziplist
    long offset;  当前ziplist中的偏移地址
    int direction;  迭代器的方向
} quicklistIter;
 表示quicklist节点中ziplist里的一个节点结构
typedef  quicklistEntry {
     指向所在quicklist的指针
    quicklistNode *node;   指向当前节点的ziplist
    unsigned char *value;   当前指向的ziplist中的节点的字符串值
    long long longval;   当前指向的ziplist中的节点的整型值
    unsigned int sz;   当前指向的ziplist中的节点的字节大小
    int offset;   当前指向的ziplist中的节点相对于ziplist的偏移量
} quicklistEntry;

创建QuickList

fill:

-5: 每个quicklist节点上的ziplist大小不能超过64 Kb。(注:1kb => 1024 bytes)

-4: 每个quicklist节点上的ziplist大小不能超过32 Kb。

-3: 每个quicklist节点上的ziplist大小不能超过16 Kb。

-2: 每个quicklist节点上的ziplist大小不能超过8 Kb。(-2是Redis给出的默认值)

-1: 每个quicklist节点上的ziplist大小不能超过4 Kb。

compress:

0 特殊值,表示不压缩

1 表示quicklist两端各有一个节点不压缩,中间的节点压缩

2 表示quicklist两端各有两个节点不压缩,中间的节点压缩

3 表示quicklist两端各有三个节点不压缩,中间的节点压缩

quicklist *quicklistCreate(void) {
    struct quicklist *quicklist;   声明指针
    quicklist = zmalloc(sizeof(*quicklist));   分配内存
    quicklist->head = quicklist->tail = NULL;    设定头尾指针
    quicklist->len = 0;   设定长度
    quicklist->count =  设定数据项总和
    quicklist->compress =  设定压缩深度
    quicklist->fill = - 设定ziplist大小限定
    return quicklist;
}

创建QuickListNode

REDIS_STATIC quicklistNode *quicklistCreateNode() {
    quicklistNode *node;
    node = zmalloc(sizeof(*node));    申请内存
    node->zl = NULL;   初始化指向ziplist的指针
    node->count =  初始化数据项个数
    node->sz =  初始化ziplist大小
    node->next = node->prev = NULL;   初始化prev和next指针
    node->encoding = QUICKLIST_NODE_ENCODING_RAW;   初始化节点编码方式
    node->container = QUICKLIST_NODE_CONTAINER_ZIPLIST;   初始化存放数据的方式
    node->recompress =  初始化再压缩标记
     node;
}

?

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读