Redis跳表(跳跃表)简介
Redis跳表是有序集合zset的底层实现基础,其主要思想对有序链表的部分节点分层,每一层都是有序链表,越上层两个节点之间的跨度越大,查询时从最上层开始,先找一个大致的范围,再向下层,找到更精确的范围,直到找到某个值。(啊如果没看过跳表结构的可能不好理解,没关系,看完以下介绍再回来细品)
Redis跳表结构
Redis跳表基本思想
Redis跳表本质是一个分层的有序链表,其结构的大致示意图如下:
底层是原始的有序链表,此时我们查找的时间复杂度为O(N)。
上图的分层结构:
第1层:在第0层上,每2个节点提取一个节点构成第1层的有序链表(这也表示:上层节点一定在下层节点中)
第2层:在第1层上,提取了1个节点
采用分层链表,查找时间复杂度降为O(logN)。以查找31为例,查找的流程为:
- 从第2层开始,31节点比21节点大,21节点的next又指向null,从21节点向下
- 31节点>21节点,31节点<41节点,因此从21节点向下
- 向后查找,找到31节点
综上,通过将有序链表的部分节点进行分层,从上层开始一次向后查找,如果本层节点的下一节点大于查找结点或者为NULL,从当前节点向下层向后查找,直到找到。再数量比较大时,可以跳过很多节点,降低时间复杂度。
Redis跳表结构
在上述基本思想的基础上,Redis跳表还加入了一些辅助的节点信息,如下图所示:
跳跃表结构体
跳跃表结构体用来管理节点、存储跳表的信息,其主要包括:
- header:指向头节点(zskiplistNode类型)
- tail:指向尾部节点
- length:跳表的长度(除头节点外的节点总数)
- level:跳表的高度
源码中,跳表结构体使用zskiplist实现:
typedef struct zskiplist{
struct zskiplistNode *header,*tail;
unsigned long length;
int level;
}
通过跳表结构体的属性,可以在O(1)时间复杂度下,获取头节点、尾节点、长度、高度。
跳跃表节点
typedef struct zskiplistNode{
sds ele; //sds类型member值
double score; //分值
struct zskiplistNode *backward; //后退指针,指向当前节点最底层的前一个节点
struct zskiplistLevel{
struct zskiplistNode *forward; //本层的下一个节点
unsigned int span; //本层每个节点和下一个节点(forward)之间的元素个数
}level[]; //柔性数组,level[i]代表跳表的第i层,每个节点有几层是在生成节点时使用算法得到的
}zskiplistNode;
头节点
头节点是一个特殊的跳表节点,它的level数组个数为固定值64(最大值),头节点中不存储member和score值(NULL和0),不计入总长度。初始化时,64层的forward指向NULL,span=0
Redis跳表的基本操作
创建跳表
- 创建跳表结构体对象zsl
- 创建头节点,并将zsl header指向这个头节点
- zsl的level初始化为1,长度初始化为0,尾节点指向NULL
插入节点
删除节点
持续更新中
Redis跳表是面试常客哦~
本文参考《Redis5 设计与源码分析》