• 「Redis数据结构」QuickList


    Redis数据结构」QuickList

    一、前言

    在前面一章,我们已经学习了ZipList压缩列表,ZipList虽然节省内存,但也引发了不少问题。

    问题1:ZipList虽然节省内存,但申请内存必须是连续空间,如果内存占用较多,申请内存效率很低。怎么办?

    ​ 答:为了缓解这个问题,我们必须限制ZipList的长度和entry大小。

    问题2:但是我们要存储大量数据,超出了ZipList最佳的上限该怎么办?

    ​ 答:我们可以创建多个ZipList来分片存储数据。

    问题3:数据拆分后比较分散,不方便管理和查找,这多个ZipList如何建立联系?

    ​ 答:Redis在3.2版本引入了新的数据结构QuickList,它是一个双端链表,只不过链表中的每个节点都是一个ZipList。


    二、概述

    在 Redis 3.0 之前,List 对象的底层数据结构是双向链表或者压缩列表。然后在 Redis 3.2 的时候,List 对象的底层改由 quicklist 数据结构实现。

    其实 quicklist 就是「双向链表 + 压缩列表」组合,因为一个 quicklist 就是一个链表,而链表中的每个元素又是一个压缩列表

    在前面讲压缩列表的时候,我也提到了压缩列表的不足,虽然压缩列表是通过紧凑型的内存布局节省了内存开销,但是因为它的结构设计,如果保存的元素数量增加,或者元素变大了,压缩列表会有「连锁更新」的风险,一旦发生,会造成性能下降。

    quicklist 解决办法,通过控制每个链表节点中的压缩列表的大小或者元素个数,来规避连锁更新的问题。因为压缩列表元素越少或越小,连锁更新带来的影响就越小,从而提供了更好的访问性能。


    三、结构

    quicklist 的结构体跟链表的结构体类似,都包含了表头和表尾,区别在于 quicklist 的节点是 quicklistNode。

    typedef struct quicklist {
        //quicklist的链表头
        quicklistNode *head;      //quicklist的链表头
        //quicklist的链表尾
        quicklistNode *tail; 
        //所有压缩列表中的总元素个数
        unsigned long count;
        //quicklistNodes的个数
        unsigned long len;       
        ...
    } quicklist;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    接下来看看,quicklistNode 的结构定义:

    typedef struct quicklistNode {
        //前一个quicklistNode
        struct quicklistNode *prev;     //前一个quicklistNode
        //下一个quicklistNode
        struct quicklistNode *next;     //后一个quicklistNode
        //quicklistNode指向的压缩列表
        unsigned char *zl;              
        //压缩列表的的字节大小
        unsigned int sz;                
        //压缩列表的元素个数
        unsigned int count : 16;        //ziplist中的元素个数 
        ....
    } quicklistNode;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    可以看到,quicklistNode 结构体里包含了前一个节点和下一个节点指针,这样每个 quicklistNode 形成了一个双向链表。但是链表节点的元素不再是单纯保存元素值,而是保存了一个压缩列表,所以 quicklistNode 结构体里有个指向压缩列表的指针 *zl。

    img

    在向 quicklist 添加一个元素的时候,不会像普通的链表那样,直接新建一个链表节点。而是会检查插入位置的压缩列表是否能容纳该元素,如果能容纳就直接保存到 quicklistNode 结构里的压缩列表,如果不能容纳,才会新建一个新的 quicklistNode 结构。

    quicklist 会控制 quicklistNode 结构里的压缩列表的大小或者元素个数,来规避潜在的连锁更新的风险,但是这并没有完全解决连锁更新的问题。


    四、小结

    QuickList的特点:

    • 是一个节点为ZipList的双端链表
    • 节点采用ZipList,解决了传统链表的内存占用问题
    • 控制了ZipList大小,解决连续内存空间申请效率问题
    • 中间节点可以压缩,进一步节省了内存
  • 相关阅读:
    pytorch基础学习(6)
    Java 成员内部类
    ESP8266-Arduino编程实例-CCS811数字气体传感器驱动
    SpringMVC: Java Web应用开发的框架之选
    jsp网上银行
    SpringBoot自动装配原理分析
    Linux学习——exec函数族和守护进程
    【手把手带你刷好题】Java刷题记录 15——>>20
    【Unity3D赛车游戏优化篇】新【八】汽车实现镜头的流畅跟随,以及不同角度的切换
    Tomcat选择不同配置文件启动
  • 原文地址:https://blog.csdn.net/u014571143/article/details/127932094