彻底弄懂Redis--set篇

Redis中有两种集合,一种是无序集合,一种是有序集合,他们之间的相同点就是不重复,不同点就是是否有序,我们分别介绍一下。

set

因为set只要保证加入的元素不重复就好,所以他的底层实现也比较简单,就是一个value为空的哈希表,key就是用来储存加入的元素值的,我们今天重点介绍的就是Sort Set(有序集合)

Sort Set

有序集合是set的升级版,在set的基础上进行了排序,所以他必定要引入一种结构来完成排序的功能,也是我们介绍的重点,跳跃表。Sort Set的底层实现就是字典+跳跃表

跳跃表的实现

跳跃表是由redis.h/zskiplistNode 和 redis.h/zskiplist 两个结构定义, 其中 zskiplistNode 结构用于表示跳跃表节点, 而 zskiplist结构则用于保存跳跃表节点的相关信息。
我们分别看一下它们的结构:

redis.h/zskiplist
typedef struct zskiplist {

    // 表头节点和表尾节点
    struct zskiplistNode *header, *tail;

    // 表中节点的数量
    unsigned long length;

    // 表中层数最大的节点的层数
    int level;

} zskiplist;
redis.h/zskiplistNode
typedef struct zskiplistNode {

    // 后退指针
    struct zskiplistNode *backward;

    // 分值
    double score;

    // 成员对象
    robj *obj;

    // 层
    struct zskiplistLevel {

        // 前进指针
        struct zskiplistNode *forward;

        // 跨度
        unsigned int span;

    } level[];

} zskiplistNode;

在跳跃表中,保存两个节点,head,tail节点,节点的数量,以及层数。节点中有一个后退指针,分值(依靠他的值进行排序),成员对象,他保存的是一个指针指向的是哈希表中的插入的具体的值,一个leve数组,你可以把他理解成一个链表,每个成员都有一个前进指针,和一个跨度。

结构介绍的差不多了,再看一下结构图:
在这里插入图片描述

这就是Redis中一个跳跃表的结构,可以根据他的源码结合理解一下,头节点是不储存值的,但是他保留着全部的层(L1-L32),这个是可以表示的最大层,并不是都会用到,然后我们向后看BW就是后退指针,1.0就是分值,o1就是保存的对象(指针);

Reids跳跃表在普通的跳跃表中做了一些改变,但是大致是相同的,如果你上面看懂了,下面一部分可以忽略,如果没看懂你可以再根据这个理解一下

在这里插入图片描述
这就是一个普通的跳跃表结构,其实大体上没有什么不同,一开始没有值,后来一行就是一层,指向这一层的下一个节点,再看每一列,他保留这这个对象参与的多层,他们的值都是一样的,有两个指针,一个是前进指针,还有后退指针。大家试着理解一下

如果你能够理解这种结构了,我们就看一下他的一些实现细节

前进指针

有上面的图可知,前进指针指向的就是某一层中后面的(比他分值大的)节点,所以也就是说我们可以通过前进指针找到最后一个节点(也就是最大的),也就是前进指针为空时,
下面是一个通过前进指针遍历所有值的一个过程图:
在这里插入图片描述
有同学可以会疑惑箭头上的数值是什么,我们这就说了

跨度

层的跨度(level[i].span 属性)用于记录两个节点之间的距离:
没错就是跨度,如果两个数之间没有跨过的数值,就是1,所以如果前进指针加上跨度就能遍历表中所有的数值,如果不用跨度也能找到最大的数值
在这里插入图片描述
我们看虚线的内容,很明显中间跨过了两个数,但是其实是走了3步到最后的,所以跨度是3,其实我们还能观察到,每一层的跨度,相加都是一样的,这是必然的,因为目标都是一样的,所以跨度也可以通过上一层的跨度相加获得。

后退指针

节点的后退指针(backward 属性)用于从表尾向表头方向访问节点: 跟可以一次跳过多个节点的前进指针不同, 因为每个节点只有一个后退指针, 所以每次只能后退至前一个节点。

图 5-6 用虚线展示了如果从表尾向表头遍历跳跃表中的所有节点: 程序首先通过跳跃表的 tail 指针访问表尾节点, 然后通过后退指针访问倒数第二个节点, 之后再沿着后退指针访问倒数第三个节点, 再之后遇到指向 NULL 的后退指针, 于是访问结束。
在这里插入图片描述

分值和成员

节点的分值(score 属性)是一个 double 类型的浮点数, 跳跃表中的所有节点都按分值从小到大来排序。

节点的成员对象(obj 属性)是一个指针, 它指向一个字符串对象, 而字符串对象则保存着一个 SDS 值。

注意节点的分值可以重复,但是对象的值是绝对不但能重复的,相同的分值会按照插入的顺序排序。
我们按照o1,o2,o3的顺序插入三个分值相同的节点
在这里插入图片描述

我介绍的就这么多了,我是看《Redis的设计与实现》学习的,有兴趣的可以看一看,写的还不错

猜你喜欢

转载自blog.csdn.net/LYue123/article/details/88813070