skiplist实现
Redis 跳跃表(Skiplist)实现详解(基于 6.0.10 版本)
跳跃表(Skiplist)是一种高效的有序数据结构,通过在节点中维护多层指针,实现快速查找、插入和删除操作(平均时间复杂度 O (logN))。Redis 中,跳跃表是有序集合(ZSet)的底层实现之一(当 ZSet 元素数量多或元素较大时使用,替代 ziplist)。本文结合 Redis 源码中的结构体,详解跳跃表的设计与工作原理。
跳跃表的核心作用
ZSet 需要支持按分值(score)排序和快速范围查询(如 ZRANGE、ZREVRANGE),跳跃表相比其他结构(如平衡树)的优势:
- 实现简单:无需像红黑树那样维护复杂的旋转平衡逻辑。
- 性能均衡:查找、插入、删除的平均时间复杂度均为 O (logN),且常数因子小。
- 适合范围查询:通过层级指针可直接定位到范围起点,高效遍历区间元素。
跳跃表的结构体设计
Redis 中跳跃表由两个核心结构体组成:zskiplistNode(节点)和 zskiplist(表本身),定义如下(简化自源码):
跳跃表节点(zskiplistNode)
typedef struct zskiplistNode {
sds ele; // 存储的元素(字符串,ZSet 中的 member)
double score; // 分值,用于排序(ZSet 中按 score 排序)
struct zskiplistNode *backward; // 后退指针(仅指向当前节点的前一个节点,用于反向遍历)
struct zskiplistLevel { // 层级数组(每个层级包含前进指针和跨度)
struct zskiplistNode *forward; // 前进指针(指向同一层级的下一个节点)
unsigned long span; // 跨度(当前节点到 forward 指向节点的"距离",用于计算排名)
} level[]; // 柔性数组(层级数量动态分配,每层独立维护指针)
} zskiplistNode;
关键字段解析:
ele:ZSet 中的成员(member),是一个字符串(sds 类型),具有唯一性(ZSet 不允许重复 member)。score:成员对应的分值,ZSet 按 score 升序排序(相同 score 的节点按ele字典序排序)。backward:后退指针,仅用于反向遍历(如ZREVRANGE),指向当前节点的前一个节点(类似双向链表的 prev 指针),但仅在最底层有效。level[]:层级数组,是跳跃表 “跳跃” 特性的核心:- 每个节点的层级数量是随机生成的(通常 1-64 层,Redis 中最大层级为 64)。
- 层级越高,前进指针跨越的节点越多,查询时可快速 “跳过” 无关节点。