redis源码解析-跳表-skiplist(6.2.3版本)

跳表,是redis中的一中链表数据结构,能够提高链表的查询效率。使用跳表,可以避免遍历一些不必要的节点,从而提高了查询,删除,插入的效率。

1,跳表结构体

typedef struct zskiplistNode {
    sds ele;//节点元素,字符串
    double score;//元素分数,在跳表中按照该分数排放
    struct zskiplistNode *backward;//上一个节点
    struct zskiplistLevel {
        struct zskiplistNode *forward//下一个跨度节点;
        unsigned long span;//跳表跨度
    } level[];//跨度数组
} zskiplistNode;//跳表节点

typedef struct zskiplist {
    struct zskiplistNode *header, *tail;//跳表头节点指针,尾节点指针
    unsigned long length;//跳表长度
    int level;//跳表最大层高
} zskiplist;//跳表

2,跳表基本操作

2.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++) {
        zsl->header->level[j].forward = NULL;
        zsl->header->level[j].span = 0;
    }
    zsl->header->backward = NULL;
    zsl->tail = NULL;
    return zsl;
}


//创建节点,创建节点对象,并赋值
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;
}

2.2,跳表释放和节点释放

//释放跳表的节点
void zslFreeNode(zskiplistNode *node) {
    sdsfree(node->ele);
    zfree(node);
}

/* 释放整个跳表 */
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);
}

2.3,插入节点

//插入一个节点
zskiplistNode *zslInsert(zskiplist *zsl, double score, sds ele) {

    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;//update数组会记录每一层最靠近新插入节点的后继节点
    unsigned int rank[ZSKIPLIST_MAXLEVEL];//rank数组 最靠近新插入节点的后继节点索引。
    int i, level;

    serverAssert(!isnan(score));
    x = zsl->header;
   // 遍历查找插入的位置,查找每一层插入的位置
    for (i = zsl->level-1; i >= 0; i--) {
        /* */
        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)))
        {
          //如果判定x节点可以前进到前一个节点,则把当前节点的跨度加到索引值上,
             //并将x对应层级指向下一个节点。
            rank[i] += x->level[i].span;//加每一层的跳跃范围
            x = x->level[i].forward;//
        }
        update[i] = x;
    }
    /*  随机生成一个跳表层级,插入之前我们应该检测下当前节点是否在hash表中存在,防止重复插入*/
    level = zslRandomLevel();
    if (level > zsl->level) {//如果随机层级大于当前最大层级,更新跳表中不存在的层高层次数据
        for (i = zsl->level; i < level; i++) {
            rank[i] = 0;
            update[i] = zsl->header;
            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 */
        x->level[i].span = update[i]->level[i].span - (rank[0] - rank[i]);
        update[i]->level[i].span = (rank[0] - rank[i]) + 1;
    }

    /* 新加的层高,不需要插入,只需要修改跨度值 */
    for (i = level; i < zsl->level; i++) {
        update[i]->level[i].span++;
    }

//第0层特化处理前向指针
    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;
}

2.4,层高随机生成算法

/*              Skiplist P = 1/4  
 *              while 循环中   
 *              循环为真的概率为 P(1/4)
 *              循环为假的概率为 (1 - P)
 *               
 *              层高为1 的概率为           (1 -P)
 *              层高为2 的概率为        P *(1 -P)
 *              层高为3 的概率为      P^2 *(1 -P)
 *              层高为4 的概率为      P^3 *(1 -P)
 *              层高为n 的概率为  P^(n-1) *(1 -P)
 * 
 *             因此平均层高为 
 *             E = 1*( 1-P) + 2*P( 1-P) + 3*(P^2) ( 1-P) + ... 
 *               = (1-P) ∑ +∞ iP^(i-1)
 *                         i=1
 *               
 *               =1/(1-P) 
 * 
 * */
int zslRandomLevel(void) {
    int level = 1;
    while ((random() & 0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
        level += 1;
    return (level < ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}

2.5,删除节点

void zslDeleteNode(zskiplist *zsl, zskiplistNode *x, zskiplistNode **update) {
    int i;
//每一层都要删除相对于的跨度和指针 
    for (i = 0; i < zsl->level; i++) {
        if (update[i]->level[i].forward == x) {
            update[i]->level[i].span += x->level[i].span - 1;
            update[i]->level[i].forward = x->level[i].forward;
        } else {
            update[i]->level[i].span -= 1;
        }
    }
//第0层特化处理
    if (x->level[0].forward) {
        x->level[0].forward->backward = x->backward;
    } else {
        zsl->tail = x->backward;
    }

    while(zsl->level > 1 && zsl->header->level[zsl->level-1].forward == NULL)
        zsl->level--;
    zsl->length--;
}

2.6,删除跳表中的某个元素

int zslDelete(zskiplist *zsl, double score, sds ele, zskiplistNode **node) {
    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
    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;
    }
    /* 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;
    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 */
}

2.7,释放链表指针

//释放节点
void zslFreeNode(zskiplistNode *node) {
    sdsfree(node->ele);
    zfree(node);
}

/* 释放整个链表 */
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);
}

2.8,修改score

涉及因为修改score导致的节点删除和节点新增

zskiplistNode *zslUpdateScore(zskiplist *zsl, double curscore, sds ele, double newscore) {
    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
    int i;

    /* We need to seek to element to update to start: this is useful anyway,
     * we'll have to update or remove it. */
//查找到需要更新的元素
    x = zsl->header;
    for (i = zsl->level-1; i >= 0; i--) {
        while (x->level[i].forward &&
                (x->level[i].forward->score < curscore ||
                    (x->level[i].forward->score == curscore &&
                     sdscmp(x->level[i].forward->ele,ele) < 0)))
        {
            x = x->level[i].forward;
        }
        update[i] = x;//找到每层需要更新的元素X
    }

    /* Jump to our element: note that this function assumes that the
     * element with the matching score exists. */
    x = x->level[0].forward;
    serverAssert(x && curscore == x->score && sdscmp(x->ele,ele) == 0);

    /* If the node, after the score update, would be still exactly
     * at the same position, we can just update the score without
     * actually removing and re-inserting the element in the skiplist. */
//如果修改的元素,位置不变,我们就不需要进行删除和插入操作,直接更新值即可
    if ((x->backward == NULL || x->backward->score < newscore) &&
        (x->level[0].forward == NULL || x->level[0].forward->score > newscore))
    {
        x->score = newscore;
        return x;
    }

    /* No way to reuse the old node: we need to remove and insert a new
     * one at a different place. */
    zslDeleteNode(zsl, x, update); //删除就节点
    zskiplistNode *newnode = zslInsert(zsl,newscore,x->ele);//插入新节点
    /* We reused the old node x->ele SDS string, free the node now
     * since zslInsert created a new one. */
    x->ele = NULL;
    zslFreeNode(x);
    return newnode;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值