zset 是通过跳跃表实现

Zset 类型的底层数据结构是由压缩列表(已放弃)或跳表实现的:

跳表是有序集合 Zset 的底层实现之⼀。
在 Redis 7.0 之前,

  • 如果有序集合的元素个数小于 128 个,并且每个元素的值小于 64 字节时,Redis 会使用压缩列表作为 Zset 的底层实现,
  • 否则会使用跳表;

在 Redis 7.0 之后,压缩列表已经废弃,交由 listpack 来替代。

跳表

image

跳表由 zskiplist 和 zskiplistNode 组成,

  • zskiplist ⽤于保存跳表的基本信息(表头、表尾、⻓度、层高等)。
    typedef struct zskiplist {
    	struct zskiplistNode *header, *tail;
    	unsigned long length;
    	int level;
    } zskiplist;
    

image

  • zskiplistNode ⽤于表示跳表节点,每个跳表节点的层⾼是不固定的,每个节点都有⼀个指向保存了当前节点的分值和成员对象的指针。
    typedef struct zskiplistNode {
    	//Zset 对象的元素值
    	sds ele;
    	//元素权重值
    	double score;
    	//后向指针
    	struct zskiplistNode *backward;
    	//节点的level数组,保存每层上的前向指针和跨度
    	struct zskiplistLevel {
    		struct zskiplistNode *forward;
    		unsigned int span;
    	} level[];
    } zskiplistNode;
    

Zset 对象要同时保存「元素」和「元素的权重」,对应到跳表节点结构里就是 sds 类型的 ele 变量和 double 类型的 score 变量。
每个跳表节点都有一个后向指针(struct zskiplistNode *backward),指向前一个节点,目的是为了方便从跳表的尾节点开始访问节点,这样倒序查找时很方便。

level 数组中的每一个元素代表跳表的一层,也就是由 zskiplistLevel 结构体表示,比如 leve[0] 就表示第一层,leve[1] 就表示第二层。

跳表是一个带有层级关系的链表,而且每一层级可以包含多个节点,每一个节点通过指针连接起来,实现这一特性就是靠跳表节点结构体中的zskiplistLevel 结构体类型的 level 数组。

level 数组中的每一个元素代表跳表的一层,也就是由 zskiplistLevel 结构体表示,比如 leve[0] 就表示第一层,leve[1] 就表示第二层。zskiplistLevel 结构体里定义了「指向下一个跳表节点的指针」和「跨度」,跨度时用来记录两个节点之间的距离。

这里的ele是存储的实际值,排序根据score
当数据量很大时,跳表的查找复杂度就是 O(logN)。

posted @ 2025-04-20 20:57  kuki'  阅读(64)  评论(0)    收藏  举报