一、什么是跳跃表,如何理解它
①生活中的例子,公交车快慢车问题
首先说个生活中的例子 ,公交车快慢车问题。
快车是站数少,停站少。慢车是逢站必停。当时我第一次见到快慢车的概念还以为是这个车跑的比较快。
正常来说,公交车分为快车和慢车。一般而言慢车可以通往目的地的每一站,而快车是只在部分站点停车。而快车又包含了慢车部分的人流量大的目的点。
所以对于我们乘客来说,我们如果想赶时间去某个地方,我们会查下地图最优线路,最快线路。一般情况下,我们会做几站快车,然后在换乘慢车到达目的地。
②跳跃表与之的联系
我们把链表看做是公交车的慢车,它包括每一个节点。我们把其中每两个节点提取出一个来,这样就成了快车,少了一半的停靠站。我们在从这个快车再继续提取,这样就又出来了更快的车。如图:
假如我们想找节点7,我们这时候 只需要 访问 1->5->7 就三次。而原来需要七次。
③redis中如何使用的
这个具体参照其他博客
https://www.jianshu.com/p/c2841d65df4c
二、redis在什么时候会使用跳跃表,为什么这个时候使用?
跳跃表的检索平均时间复杂度为O(logN),可以与红黑树媲美。而实现又相对容易,因为在链表基础上加上多层索引就好。而多层索引的写入势必会相对链表需要更多的空间。
扫描二维码关注公众号,回复:
11549782 查看本文章
所以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的实现原理是:
从表尾向表头遍历压缩列表,查找给定的成员,沿途记录经过的节点的数量,当找到给定成员之后,途径节点的数量就是该元素的排名。