Redis中的跳跃表(skiplist)是有序集合(Sorted Set)数据结构的底层实现之一,尤其在元素数量较多或元素长度较大时(超过配置阈值,如zset-max-ziplist-entries
和zset-max-ziplist-value
),Redis会选择使用跳跃表而不是压缩列表(ziplist)来存储数据。跳跃表是一种可以在对数时间内完成查找、插入和删除操作的数据结构,它通过在原有的链表结构基础上增加多级索引来实现高效的访问。
数据结构
Redis中的跳跃表主要由两个结构体定义:
-
zskiplistNode:表示跳跃表中的节点,每个节点包含:
- 分数值(score),用于排序。
- 成员对象(member)。
- 各层级的前进指针(level[]),用于快速跳跃。
- 后退指针(backward),指向该节点在上一级的前一个节点。
-
zskiplist:表示整个跳跃表,包含:
- 头节点(header)和尾节点(tailer)。
- 节点数量(length)。
- 表示跳跃表层级的最高层数(level)。
- 每个层级中随机生成的跨度(span)。
核心特性
-
多级索引:每个节点可能在多个层次出现,高层的节点是低层节点的子集,这样的设计允许在查找时进行“跳跃式”前进,大幅减少比较次数。
-
随机层数:新插入的节点的层数是随机决定的(通常是1到某个最大值之间,Redis中这个最大值为64),这样可以保证跳跃表的平衡性,同时保持较高的查找效率。
-
有序性:跳跃表中的节点按照分数值从小到大排序,且同一分值的不同成员按插入顺序排序。
-
时间复杂度:平均情况下,跳跃表的查找、插入和删除操作的时间复杂度均为O(log n)。
实现细节
-
查找:从跳跃表的最顶层开始,通过比较节点的分值,快速向下“跳跃”,直到找到目标节点或到达合适的插入位置。
-
插入:新节点的层数是随机生成的,然后在每一层找到合适的位置插入,并更新相邻节点的指针。
-
删除:首先定位到待删除节点,然后从最高层开始,逐层调整指针,移除该节点,最后释放节点资源。
-
内存管理:节点的分配和释放通过Redis自身的内存管理机制进行,确保高效且不易产生碎片。
源码分析
源码中,跳跃表的实现位于t_zset.c
文件中,其中zslCreate
用于创建一个新的跳跃表,zslInsert
用于插入新节点,zslDelete
用于删除节点,zslGetElementByRank
和zslGetRank
等函数用于根据排名或分值查找元素。通过阅读这些函数,可以深入了解跳跃表的实现细节及操作逻辑。
跳跃表的实现体现了Redis对数据结构优化的重视,特别是在处理大量有序数据时,能够提供良好的性能表现。