Redis

关注公众号 jb51net

关闭
首页 > 数据库 > Redis > Redis SkipList

Redis底层数据结构SkipList的实现

作者:WARRIOR

本文主要介绍了Redis底层数据结构SkipList的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

为什么需要 SkipList(跳表)

在普通链表中查找元素的时候,因为需要遍历查找,所以查询效率非常低,时间复杂度是O(N)。

因此我们需要跳表。跳表是在链表基础上改进过来的,实现了一种 “多层”有序 链表,这样的好处是能快速定位数据。

跳表的结构设计

如下图所示,这是一个层级为3的跳表

image.png

和普通的双向链表一样,SkipList 都有一个指向上一个节点的指针,也有一个指向下一个节点的指针。

但是你会发现,在层次为 3 的跳表中,会有三级指针的存在,而且 SkipList 中元素会按照 score 值升序排序(score 值一样则按照 ele 排序)

这样的设计会带来什么好处呢?

跳表的节点(zskiplistNode )

我们来看看跳表的结构体源码

typedef struct zskiplistNode {
    sds ele;
    double score;
    struct zskiplistNode *backward;
    struct zskiplistLevel {
        struct zskiplistNode *forward;
        unsigned long span;
    } level[];
} zskiplistNode;

其中,

level[] 就是实现跳表多层次指针的关键所在,level 数组中的每一个元素代表跳表的一层,比如 leve[0] 就表示第一层,leve[1] 就表示第二层。zskiplistLevel 结构体里定义了指向下一个跳表节点的指针** *forward 和用来记录两个节点之间的距离 span,如图所示,

image.png

💡 span 跨度有什么用?

跳表(zskiplist )

跳表的结构如下

    typedef struct zskiplist {
        struct zskiplistNode *header, *tail;
        unsigned long length;
        int level;
    } zskiplist;

跳表的查询过程

在使用 ZRANGEBYSCORE key min max进行范围查询的时候

可在 t_zset.c#genericZrangebyscoreCommand(zrange_result_handler *handler, zrangespec *range, robj *zobj, long offset, long limit, int reverse) 查看源码

zslFirstInRangezslLastInRange 类似)中,我们就能看到跳表根据 scroe 值的查询过程,源码如下:

    /* Find the first node that is contained in the specified range.
     * Returns NULL when no element is contained in the range. */
    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. */
            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->level[0].forward;
        serverAssert(x != NULL);
        /* Check if score <= max. */
        if (!zslValueLteMax(x->score,range)) return NULL;
        return x;
    }

该函数执行逻辑如下:

简单来说在 SkipList 中找到一个元素的步骤如下:

  • 从跳表的顶层开始,从左到右遍历指针,直到找到一个指针指向的值大于或等于目标元素。记录下该位置作为当前位置。
  • 如果当前位置指向的值等于目标元素,则找到了目标元素,搜索结束
  • 如果当前位置的下一个指针存在且指向的值小于目标元素,将当前位置向右移动到下一个指针所指向的位置。重复步骤1。
  • 如果当前位置的下一个指针不存在或指向的值大于目标元素,将当前位置向下移动一层。重复步骤1。

到此这篇关于Redis底层数据结构SkipList的实现的文章就介绍到这了,更多相关Redis SkipList内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文