redis吸引不少人使用的一个重要的缘由,就是它对众多数据类型的支持。包括string,hash,set,zset,list等五种数据对象。redis
其中zset用来保证数据的有序存储,实现中,redis使用跳跃表和压缩列表,做为zset的底层实现。当元素数量比较多,或者元素成员是比较长的字符串时,底层实现采用跳跃表。算法
跳跃表是什么?数组
一种有序的数据结构,经过在节点中维持多个指向其它节点的指针,达到快速访问的目的。数据结构
跳跃表的好处是什么? ide
1. 跟平衡树相比,实现简单;spa
2. 平均复杂度为O(logN),最坏为O(N);指针
跳跃表的数据结构由 zskiplistNode 和 zskiplist 两个结构定义,zskiplistNode表示跳跃表节点,zskiplist保存跳跃表节点的相关信息。对象
zskiplistNode的数据结构以下:blog
typedef struct zskiplistNode {排序
robj *obj;
double score;
struct zskiplistNode *backward;
struct zskiplistLevel {
struct zskiplistNode *forward;
unsigned int span;
} level[];
} zskiplistNode
skiplistLevel包含多个元素,每一个元素包含一个指向其它节点的前进指针,经过它来加快访问其它节点。每次建立一个跳跃表节点的时候,会随机生成一个值,该值介于1和32之间,做为level数组的大小。span表示层的跨度,用来计算最终的位置,程序在遍历时,会将沿途访问过的全部层的跨度累计,得出目标节点的对应位置;
backward用于从表尾向表头访问节点;
score 全部节点都按照它进行排序;
obj 指向一个字符串对象;
zskiplist的数据结构:
typedef struct zskiplist {
struct zskiplistNode *header, *tail;
unsigned long length;
int level;
} zskiplist;
header,tail用来表示跳跃表的表头和表尾巴节点,length表示跳跃表的总体长度,level表示跳跃表的高度。
zskiplist的主要做用,是能够方便的对整个跳跃表进行处理,好比获取跳跃表的整个长度的信息。
跳跃表的层数如何生成?
在插入的过程当中构造,向跳跃表中插入一个数值,至关于在表中插入一列从起始跳跃表节点S出发,向上的一段数值,须要肯定两个元素:数值的位置和层数。因为全部链是递增序列的方式,因此位置主要是根据对应数值的比较对出。而层数,程序会先记录比该节点小的值的span,而后,随机生成介于1和32之间的数值,做为该节点的层数,该算法主要是基于冥次定律,越大的数出现的几率越小。假设level为2的几率为P,则level为3的几率为p*p,以此类推。
跳跃表查找的示例图:
图中,表示有三个节点,他们对应的score分别为1,2,3,其中,虚线,表明在跳跃表中,查找score为2,成员对象为02的节点,在查找过程当中,通过了两个span为1的节点,因此,能够得出该节点在跳跃表中的排位为2。 BW表示后退指针,实线的,表示节点间的span,因为节点间的关系比较多,因此只画了几个。