redis有序集合zset的实现方式 跳表

有序集合zset

之前在使用redis的时候就发现redis的有序集合十分方便,再写关于排行类的统计存储时,用mysql十分麻烦,而用reids的有序集合就十分的方便,然后就想看看他的底层实现,而却发现他没有使用AVL,红黑树等实现,而是使用了一种叫做跳表的数据结构,却是一种十分的简单的数据结构

跳表

跳表是一种相对简单的平衡数据结构 跳表与AVL、红黑树等相比,数据结构简单,算法易懂,但查询的时间复杂度与平衡二叉树/红黑树相当。

跳表的原理#

跳跃表的思想来自于一篇论文:Skip Lists: A Probabilistic Alternative to Balanced Trees. 如果想要深入了解跳跃表,可以阅读论文原文。这里引用论文中的一幅图对跳跃表的原理作一个简单的说明。
redis有序集合zset的实现方式 跳表_第1张图片其中
: a 表:单链表:查询时间复杂度O(n)
: b 表:level-2单链表:每隔一个节点为一个level-2节点,每个level-2节点有2个后继指针,分别指向单链表中的下一个节点和下一个level-2节点。查询时间复杂度为O(n/2)
: c 表:level-3单链表:每隔一个节点为一个level-2节点,每隔4个节点为一个level-3节点,查询时间复杂度O(n/4)
: d 表:指数式单链表:每2^i个节点的level为i+1,查询时间复杂度为O(log2N)
: e 表:跳跃表:各个level的节点个数同指数式单链表,但出现的位置随机,查询复杂度仍然是O(log2N)吗

之所以这里关心查询复杂度,因为有序链表的插入和删除复杂度等于查询复杂度。
作为一种概率性算法,文章证明了跳跃表查询复杂度的期望是O(logN).
(摘自https://blog.csdn.net/da_kao_la/article/details/94744886)

跳表的工作原理

redis有序集合zset的实现方式 跳表_第2张图片
查找9 因为顶部的 节点1开始查找到20 9<20 回到节点2 同理回到节点3 查找到5<9 5开始查找
之后找到9

而插入也是随机插入几级节点,因为随机性我们假设插入13而级数为4插入方式如图所示
redis有序集合zset的实现方式 跳表_第3张图片redis有序集合zset的实现方式 跳表_第4张图片然后删除节点9也是十分清晰
redis有序集合zset的实现方式 跳表_第5张图片redis有序集合zset的实现方式 跳表_第6张图片跳表的源码

/* ZSETs use a specialized version of Skiplists */
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;

typedef struct zset {
    dict *dict;
    zskiplist *zsl;
} zset;
————————————————
版权声明:本文为CSDN博主「da_kao_la」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/da_kao_la/article/details/94744886

Redis使用跳表的原因

Redis 中的有序集合支持的核心操作主要有以下几个:

插入
删除
查找
按照区间查找数据
迭代输出有序序列

其中,插入、删除、查找以及迭代输出有序序列这几个操作,红黑树也可以完成,时间复杂度和跳表是一样的。

但是,按照区间查找数据这个操作,红黑树的效率没有跳表高。跳表可以在 O(logn)O(logn) 时间复杂度定位区间的起点,然后在原始链表中顺序向后查询就可以了,这样非常高效。

此外,相比于红黑树,跳表还具有代码更容易实现、可读性好、不容易出错、更加灵活等优点,因此 Redis 用跳表来实现有序集合。

你可能感兴趣的:(学习记录)