Redis源码剖析---跳表

基于Redis7
Redis中的zset(有序集合)是使用跳表来实现的

结构体的定义

跳表节点的定义


//如果score相等,再按照ele排序
typedef struct zskiplistNode {
    sds ele;//节点存储的具体的值
    double score;//存储的分值,我们就是按照这个分值来进行排序的
    struct zskiplistNode *backward;//后退指针,用于指向前一个节点,进行反向遍历(zrerange)
    struct zskiplistLevel {
        struct zskiplistNode *forward;//用于指向当前层后一个节点,如果他指向nullptr,说明后面没有节点
        //span实际上是用来计算rank(排位的),当前值在zset中的排位,
        //计算排名就是从到开始到当前节点的路径的一个累加
        unsigned long span;//到(同一层级)到下一个节点的跨度,用来记录两个节点之间的距离,跨度很大说明离得很远
    } level[];//这个是一个柔性数组,层数不固定
} zskiplistNode;

跳表的定义

//跳表的结构
typedef struct zskiplist {
    struct zskiplistNode *header, *tail;//头指针,这个是实实在在的一个节点,但是没有存储有效的元素,尾节点,指nullptr
    unsigned long length;//所建立跳表的长度
    int level;//当前有多少层数
} zskiplist;

区间的定义

//用于表示有序集合的数值范围,包括最大值和最小值
typedef struct {
    double min, max;//最小值和最大值
    int minex, maxex; /* are min or max exclusive? *///minex和maxex表示最大值和最小值是否不包含在范围内(是否是开闭区间)
    //minex表示最小值不包含在范围内,maxex表示最大值不包含在里面
} zrangespec;

跳表的实现

  1. 创建一个跳表的节点
/* Create a skiplist node with the specified number of levels.
 * The SDS string 'ele' is referenced by the node after the call. */
//创建一个跳表中的节点,level设置为最大的层数,score就是对应的节点的分数,ele就是该节点存储的value
zskiplistNode *zslCreateNode(int level, double score, sds ele) {
    zskiplistNode *zn =
        zmalloc(sizeof(*zn)+level*sizeof(struct zskiplistLevel));//动态分配一个节点
    zn->score = score;
    zn->ele = ele;
    return zn;
}
  1. 创建出一个跳表
//创建一个跳表
zskiplist *zslCreate(void) {
    int j;
    zskiplist *zsl;

    zsl = zmalloc(sizeof(*zsl));
    zsl->level = 1;//一开始就只建立一层
    zsl->length = 0;//此时一个节点都没有
    zsl->header = zslCreateNode(ZSKIPLIST_MAXLEVEL,0,NULL);//给头分配了一个头节点,这里设置默认最高的一个层数
    for (j = 0; j < ZSKIPLIST_MAXLEVEL; j++) {//32层都要给尾指针建立连接关系
        zsl->header->level[j].forward = NULL;
        zsl->header->level[j].span = 0;//后面没有节点,所以跨度为0
    }
    zsl->header->backward = NULL;//给头节点的前驱也要设置为null
    zsl->tail = NULL;
    return zsl;
}
  1. 释放跳表中的节点
/* Free the specified skiplist node. The referenced SDS string representation
 * of the element is freed too, unless node->ele is set to NULL before calling
 * this function. */
void zslFreeNode(zskiplistNode *node) {
    sdsfree(node->ele);
    zfree(node);
}
  1. 释放整个跳表

/* Free a whole skiplist. */
void zslFree(zskiplist *zsl) {
    zskiplistNode *node = zsl->header->level[0].forward, *next;

    zfree(zsl->header);
    while(node) {
        next = node->level[0].forward;
        zslFreeNode(node);
        node = next;
    }
    zfree(zsl);
}
  1. 随机获得一个层数
int zslRandomLevel(void) {
    static const int threshold = ZSKIPLIST_P*RAND_MAX;//这个地方是2^64次方,才能到达32层
    int level = 1;
    while (random() < threshold)//一个随机数
        level += 1;
    return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}
  1. 增加一个节点
/* Insert a new node in the skiplist. Assumes the element does not already
 * exist (up to the caller to enforce that). The skiplist takes ownership
 * of the passed SDS string 'ele'. */
zskiplistNode *zslInsert(zskiplist *zsl, double score, sds ele) {
    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;//这个地方也需要记录所有的下层节点,他们的span需要加一,最大层数是32
    unsigned long rank[ZSKIPLIST_MAXLEVEL];//查找时每一层跨越节点的排名
    int i, level;

    serverAssert(!isnan(score));
    x = zsl->header;
    for (i = zsl->level-1; i >= 0; i--) {
        /* store rank that is crossed to reach the insert position ,获得需要插入的位置*/
        rank[i] = i == (zsl->level-1) ? 0 : rank[i+1];//如果不是最顶层,该层的起始值就是上一层的树
        while (x->level[i].forward &&
                (x->level[i].forward->score < score ||
                    (x->level[i].forward->score == score &&
                    sdscmp(x->level[i].forward->ele,ele) < 0)))
        //往后走
        {
            rank[i] += x->level[i].span;//当前的rank需要加少这个x的rank
            x = x->level[i].forward;//x需要往后走
        }
        update[i] = x;//记录下层前的节点
    }
    /* we assume the element is not already inside, since we allow duplicated
     * scores, reinserting the same element should never happen since the
     * caller of zslInsert() should test in the hash table if the element is
     * already inside or not. */
    //
    level = zslRandomLevel();//随机生成新节点所需要建立索引的树的高度

    if (level > zsl->level) {
        //如果获得的level比已经建立好的索引还要大
        for (i = zsl->level; i < level; i++) {
            //从当前的level到获得的随机的level都需要建立一个索引
            rank[i] = 0;//后面需要添加元素
            update[i] = zsl->header;//update需要成为头节点
            update[i]->level[i].span = zsl->length;//跨度就是整个链表的长度
        }
        zsl->level = level;//更新最大的层数
    }
    x = zslCreateNode(level,score,ele);//创建一个新的节点
    //增加节点的时候,重新建立连接关系
    for (i = 0; i < level; i++) {
        //链表的插入算法
        x->level[i].forward = update[i]->level[i].forward;
        update[i]->level[i].forward = x;

        /* update span covered by update[i] as x is inserted here */
        //更新span值
        x->level[i].span = update[i]->level[i].span - (rank[0] - rank[i]);//更新节点的span值
        update[i]->level[i].span = (rank[0] - rank[i]) + 1;
    }

    /* increment span for untouched levels */
    for (i = level; i < zsl->level; i++) {
        update[i]->level[i].span++;//从level到最高层的span都需要增加
    }

    x->backward = (update[0] == zsl->header) ? NULL : update[0];//
    if (x->level[0].forward)
        x->level[0].forward->backward = x;
    else
        zsl->tail = x;
    zsl->length++;
    return x;
}
  1. 删除跳表中的节点
//删除一个节点,x就是要删除的节点
void zslDeleteNode(zskiplist *zsl, zskiplistNode *x, zskiplistNode **update) {
    int i;
    for (i = 0; i < zsl->level; i++) {
        if (update[i]->level[i].forward == x) {//这个就是最接近要删除的x那一层
            update[i]->level[i].span += x->level[i].span - 1;//加上x的span,同时因为要去掉x这个元素,所以要减1
            update[i]->level[i].forward = x->level[i].forward;//直接把x进行删除
        } else {
            update[i]->level[i].span -= 1;//不是在同一层,他的update直接-1即可
        }
    }
    //这个上面我们只是把next节点修改,向前指针没修改
    //处理边界条件
    //如果被删除节点不是尾节点的时候
    if (x->level[0].forward) {
        x->level[0].forward->backward = x->backward;//把后一个节点的前驱指向x的前一个节点
    } else {
        zsl->tail = x->backward;//否则,就是一个尾节点,更新尾指针
    }
    //如果顶层空了
    while(zsl->level > 1 && zsl->header->level[zsl->level-1].forward == NULL)
        zsl->level--;//我们需要将层数进行减少
    zsl->length--;//长度也需要减少
}

//node就是返回删除的节点,先查找到相应的节点将其进行保存,后执行删除节点的操作
int zslDelete(zskiplist *zsl, double score, sds ele, zskiplistNode **node) {
    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;//使用update记录进行搜索对应元素的时候每次进行下层的节点,一位进行下层,那么他们的rank值就会发生改变 ,每一层的节点的span都需要-1
    int i;

    x = zsl->header;
    for (i = zsl->level-1; i >= 0; i--) {
        while (x->level[i].forward &&
                (x->level[i].forward->score < score ||
                    (x->level[i].forward->score == score &&
                     sdscmp(x->level[i].forward->ele,ele) < 0)))
        {
            x = x->level[i].forward;
        }
        update[i] = x;//记录该层p的前一个节点,用来进行修改连接关系,以及修改span值
    }
    /* We may have multiple elements with the same score, what we need
     * is to find the element with both the right score and object. */
    x = x->level[0].forward;//这个地方的x就可能是要删除的节点
    //进行删除该节点
    if (x && score == x->score && sdscmp(x->ele,ele) == 0) {//说明符合我们锁需要删除的元素
        zslDeleteNode(zsl, x, update);//执行删除节点从链表中
        if (!node)
            zslFreeNode(x);
        else
            *node = x;
        return 1;
    }
    return 0; /* not found */
}

  1. 获得相应的排名
//查找就是分为向前(同层),向下(下层)
//查找zset某个元素在里面的排名
unsigned long zslGetRank(zskiplist *zsl, double score, sds ele) {
    zskiplistNode *x;
    unsigned long rank = 0;//初始化排名为0
    int i;

    x = zsl->header;//先获得头节点
    for (i = zsl->level-1; i >= 0; i--) {//从最顶层开始往底层走
        while (x->level[i].forward &&
            (x->level[i].forward->score < score ||
                (x->level[i].forward->score == score &&
                sdscmp(x->level[i].forward->ele,ele) <= 0)))//当前节点的下一个有节点分数小于我们需要的分数,或者元素相等的时候,ele相等,也要往下走
                 {
            rank += x->level[i].span;//加上当前节点的rank值,排名就是span的总和
            x = x->level[i].forward;//x就需要往前走
        }

        /* x might be equal to zsl->header, so test if obj is non-NULL */
        if (x->ele && x->score == score && sdscmp(x->ele,ele) == 0) {//如果元素存在,分数是我们需要的分数,同时value也是我们需要的分数
            return rank;//那我们就返回对应的排名
        }
        //没找到,或者已经往前走了,就需要进行下沉
    }
    return 0;
}
  1. 判断是否存在与给定范围匹配的节点
int zslValueGteMin(double value, zrangespec *spec) {
    //把节点的最大值和范围的最小值进行比较,因为最小值可能存在可能不存在,所以需要进行判断是否包含最小值
    return spec->minex ? (value > spec->min) : (value >= spec->min);
}

int zslValueLteMax(double value, zrangespec *spec) {
    return spec->maxex ? (value < spec->max) : (value <= spec->max);
}

/* Returns if there is a part of the zset is in range. */
//返回是否存在与给定范围匹配的节点
int zslIsInRange(zskiplist *zsl, zrangespec *range) {
    zskiplistNode *x;

    /* Test for ranges that will always be empty. */
    //给定的区间是否不存在
    if (range->min > range->max ||
            (range->min == range->max && (range->minex || range->maxex)))
        return 0;
    x = zsl->tail;//获得尾节点,尾巴节点是最大的分数
    if (x == NULL || !zslValueGteMin(x->score,range))
        return 0;
    x = zsl->header->level[0].forward;
    if (x == NULL || !zslValueLteMax(x->score,range))
        return 0;
    //范围合理,返回成功
    return 1;
}
  1. 在给定范围的第一个节点
 zskiplistNode *zslFirstInRange(zskiplist *zsl, zrangespec *range) {
    zskiplistNode *x;
    int i;

    /* If everything is out of range, return early. */
    //对跳表进行范围检查,发现没有存在,所以直接返回
    if (!zslIsInRange(zsl,range)) return NULL;

    x = zsl->header;
    for (i = zsl->level-1; i >= 0; i--) {
        /* Go forward while *OUT* of range. */
        //查找符合min项的节点
        while (x->level[i].forward &&
            !zslValueGteMin(x->level[i].forward->score,range))
                x = x->level[i].forward;
    }

    /* This is an inner range, so the next node cannot be NULL. */
    //这个x就是在范围内的第一个节点
    x = x->level[0].forward;
    serverAssert(x != NULL);//保证不为空

    /* Check if score <= max. */
    //同时要判断是不是应该也要小于最大值
    if (!zslValueLteMax(x->score,range)) return NULL;
    //都满足,就返回该节点
    return x;
}
  1. 根据范围进行删除跳表的元素
 * sorted set, in order to remove the elements from the hash table too. */
//范围删除
unsigned long zslDeleteRangeByScore(zskiplist *zsl, zrangespec *range, dict *dict) {
    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
    unsigned long removed = 0;
    int i;

    x = zsl->header;
    for (i = zsl->level-1; i >= 0; i--) {
        while (x->level[i].forward &&
            !zslValueGteMin(x->level[i].forward->score, range))
                x = x->level[i].forward;
        update[i] = x;
    }

    /* Current node is the last with score < or <= min. */
    //定位到给定范围的第一个节点
    x = x->level[0].forward;

    /* Delete nodes while in range. */
    while (x && zslValueLteMax(x->score, range)) {
        zskiplistNode *next = x->level[0].forward;//记录下一个节点
        zslDeleteNode(zsl,x,update);//删除给定的节点
        dictDelete(dict,x->ele);//释放值的空间
        zslFreeNode(x); /* Here is where x->ele is actually released. *///释放节点
        removed++;
        x = next;
    }
    return removed;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zevin~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值