redis底层数据结构-跳跃表(skiplist)

一、什么是跳跃表,如何理解它

①生活中的例子,公交车快慢车问题

首先说个生活中的例子 ,公交车快慢车问题。

快车是站数少,停站少。慢车是逢站必停。当时我第一次见到快慢车的概念还以为是这个车跑的比较快。

正常来说,公交车分为快车和慢车。一般而言慢车可以通往目的地的每一站,而快车是只在部分站点停车。而快车又包含了慢车部分的人流量大的目的点。

所以对于我们乘客来说,我们如果想赶时间去某个地方,我们会查下地图最优线路,最快线路。一般情况下,我们会做几站快车,然后在换乘慢车到达目的地。

②跳跃表与之的联系

我们把链表看做是公交车的慢车,它包括每一个节点。我们把其中每两个节点提取出一个来,这样就成了快车,少了一半的停靠站。我们在从这个快车再继续提取,这样就又出来了更快的车。如图:

假如我们想找节点7,我们这时候 只需要 访问  1->5->7 就三次。而原来需要七次。

③redis中如何使用的

这个具体参照其他博客

https://www.jianshu.com/p/c2841d65df4c

二、redis在什么时候会使用跳跃表,为什么这个时候使用?

跳跃表的检索平均时间复杂度为O(logN),可以与红黑树媲美。而实现又相对容易,因为在链表基础上加上多层索引就好。而多层索引的写入势必会相对链表需要更多的空间。

所以redis在数据对象比较大或者数据对象比较多的时候才会选择跳跃表。而数据量较少的时候会选择压缩列表。

还有需要注意的一点,数据必须为有序的。

三、有序集合中的zrevrange是怎么实现的,也就是说排行榜是怎么实现的?

首先数据结构有压缩列表和跳跃表。

看下跳跃表实现的代码

unsigned long zslGetRank(zskiplist *zsl, double score, sds ele) {
    zskiplistNode *x;
    unsigned long rank = 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))) {
            rank += x->level[i].span;
            x = x->level[i].forward;
        }

        /* x might be equal to zsl->header, so test if obj is non-NULL */
        if (x->ele && sdscmp(x->ele, ele) == 0) {
            return rank;
        }
    }
    return 0;
}

可以发现是从最上层查找元素用来计算rank值的。

看下《redis设计与实现》里的一段话

ziplist 和  skiplist ZREVRANK的实现原理是:

从表尾向表头遍历压缩列表,查找给定的成员,沿途记录经过的节点的数量,当找到给定成员之后,途径节点的数量就是该元素的排名。

猜你喜欢

转载自blog.csdn.net/wuye_lh/article/details/107332222