🌷 zSkipList

吞佛童子2022年10月10日
  • db
  • Redis
  • Sorted Set
大约 2 分钟

🌷 zSkipList

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;

// 有序集合 = dict + zskiplist
typedef struct zset {
    dict *dict;
    zskiplist *zsl;
} zset;

2. zskiplist

  • 跳表特点
    • 查找、增加、删除 复杂度 O(logN),无维护平衡操作,对跳表的修改只发生在 局部
    • 实现比 红黑树 要简单一些
    • 最底层存放所有有序节点,可快速 范围查询
    • Redis 的 zskiplist 还额外保存了 span 遍历,可快速得到某个 节点的排名

1) 随机层数

// 获取节点所处的层数
// ZSKIPLIST_MAXLEVEL = 32
// 
// 所有节点肯定会在 第一层 出现
// 第 2 层的概率 = 0.5;第 3 层的概率为 0.25;...;第 32 层的概率为 1/2^31
int zslRandomLevel(void) {
    static const int threshold = ZSKIPLIST_P*RAND_MAX;
    int level = 1;
    while (random() < threshold) // 随机数 处于 概率范围内,可以进入上一层
        level += 1;
    return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}

2) 创建跳表

zskiplist *zslCreate(void) {
    int j;
    zskiplist *zsl;

    zsl = zmalloc(sizeof(*zsl)); // 申请内存空间
    zsl->level = 1; // 初始层数为 1
    zsl->length = 0; // 节点数为 0
    zsl->header = zslCreateNode(ZSKIPLIST_MAXLEVEL,0,NULL); // 创建一个 层数为 32,权重为 0,值为 null 的头节点
    // 头节点初始化 - 每层节点进行初始化
    for (j = 0; j < ZSKIPLIST_MAXLEVEL; j++) {
        zsl->header->level[j].forward = NULL; // 前置节点指针为 null
        zsl->header->level[j].span = 0; // 跨度为 0
    }
    zsl->header->backward = NULL;
    zsl->tail = NULL;
    return zsl;
}

3) 插入节点

zskiplistNode *zslInsert(zskiplist *zsl, double score, sds ele) {
    zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x; // 节点路径
    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))) // 这里还比较了 值 的大小,因此不会出现 权重相等 时 O(N) 的复杂度
        {
            rank[i] += x->level[i].span;
            x = x->level[i].forward;
        }
        update[i] = x;
    }

    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;
    }

    /* increment span for untouched levels */
    for (i = level; i < zsl->level; i++) {
        update[i]->level[i].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; // 返回
}
上次编辑于: 2022/10/10 下午8:43:48
贡献者: liuxianzhishou