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的设计与实现》学习的,有兴趣的可以看一看,写的还不错