Redis使用跳跃表作为有序集合键的底层实现之一,如果一个有序集合包含的元素数量比较多,又或者有序集合中元素的成员是比较长的字符串时,Redis就会使用跳跃表来作为有序集合键的底层实现
Redis两个地方用到跳跃表:
- 有序集合键
- 集群节点
一、跳跃表的实现
两个结构定义:
- zskiplistNode(跳跃表节点)
- zskiplist(保存跳跃表节点相关信息)
1.1 zskiplist
- header:指向跳跃表的表头节点
- tail:指向跳跃表的表尾节点
- level:记录目前跳跃表内,层数最大的那个节点的层数(表头节点的层数不计算)
- length:跳跃表的长度,即目前包含节点的数量(表头节点不计算)
typedef struct zskiplist{
struct zskiplistNode *header,*tail;
unsigned long length;
int level;
}zskiplist;
1.2 zskiplistNode
typedef struct zskiplistNode{
struct zskiplistlevel{
struct zskiplistNode * forward;
unsigned int span;
}level[];
struct zskiplistNode *backward;
double score;
robj *obj
}zskiplistNode;
level:
level数组包含多个元素,每个元素都包含一个指向其他zskiplistNode节点的指针,一般来说层的数量越多,访问其他节点的速度越快,每次创建一个新的跳跃表节点的时候,随机生成一个介于1-32之间的值N,作为level数组的大小(越大的数出现概率越小),这个大小就是层的高度,从第1层连续创建到第N层。
前进指针(forward):
每个层都有一个指向表尾方向的指针
跨度(span):
层的跨度用于记录两个节点的举例,指向NULL时跨度为0。跨度与遍历无关,用来计算排位(rank):查找某个节点的过程中,将沿途访问的所有层的跨度累加,就是目标节点在跳跃表的排位
后退指针(backward):
用于表尾方向向表头方向访问节点,每个节点只有一个后退指针,只能后退至前一个节点
分值和成员(score和obj):
score是double类型的浮点数,跳跃表中的所有节点都按分值从小到大来排序,不同节点score可以相同(从小到大排)。obj是一个指针,指向字符串对象,字符串对象保存SDS值,各节点的obj唯一
左一为zskiplist结构,其他为zskiplistNode结构,表头节点层数默认32?。