Redis中的强大的数据结构跳跃表(skiplist)的内部详解及实际运用

栏目: 数据库 · 发布时间: 6年前

内容简介:跳跃表结构在跳跃表结构如下:的值,这个值在跳跃表中是按照大小排好序的。

跳跃表结构在 Redis 中的运用场景只有一个,那就是作为有序列表 (Zset) 的使用。跳跃表的性能可以保证在查找,删除,添加等操作的时候在对数期望时间内完成,这个性能是可以和平衡树来相比较的,而且在实现方面比平衡树要优雅,这就是跳跃表的长处。跳跃表的缺点就是需要的存储空间比较大,属于利用空间来换取时间的数据结构。接下来我们思考三个问题:

思考三个问题

  • 跳跃表的底层结构是什么样的,为什么可以支撑它在对数期望时间内完成基本操作(增删改查)?
  • 在跳跃表中,完成一个元素的增删改查的详细过程是怎样的?
  • 利用跳跃表作为底层数据结构的有序列表,在实际的业务场景中有什么运用?

跳跃表结构

跳跃表结构如下:

Redis中的强大的数据结构跳跃表(skiplist)的内部详解及实际运用
在跳跃表中,每个跳跃表的节点都会维护着一个 score

的值,这个值在跳跃表中是按照大小排好序的。

跳跃表的数据结构源代码

typedef struct zskiplist {

    // 头节点,尾节点
    struct zskiplistNode *header, *tail;

    // 节点数量
    unsigned long length;

    // 目前表内节点的最大层数
    int level;

} zskiplist;
复制代码
  • header 指向了跳跃表的头结点,tail 指向跳跃表的尾节点
  • length 表示了跳跃表节点中的数量
  • level 表示跳跃表的表内节点的最大层数

跳跃表的节点结构如下图所示

typedef struct zskiplistNode {

    // member 对象
    robj *obj;

    // 分值
    double score;

    // 后退指针
    struct zskiplistNode *backward;

    // 层
    struct zskiplistLevel {

        // 前进指针
        struct zskiplistNode *forward;

        // 这个层跨越的节点数量
        unsigned int span;

    } level[];

} zskiplistNode;
复制代码

直观来感受下跳跃表结构

Redis中的强大的数据结构跳跃表(skiplist)的内部详解及实际运用
  • obj (成员对象) :对应的是图中的 o1, o2, o3,是用来存储一个节点中的对象的。
  • score (分值) :对应的是每一个成员对象中的 1.0,2.0 等分数值。
  • 后退指针 :这个指针指向的是前面的一个跳表节点。
  • :这个结构包括前进指针和记录了跨越的节点数量,这块就是跳跃表的精髓所在。

跳跃表的基本结构就是上面所展示的部分,接下来我们开始进行分析跳跃表的基础操作过程(增删改查)

跳跃表增删查改过程

Redis中的强大的数据结构跳跃表(skiplist)的内部详解及实际运用

一个跳跃表的一个节点是 64 层,能够存储的节点数量应该 2^64 个。在源码中是这样的,官方没有其他的解释。

define ZSKIPLIST_MAXLEVEL 64 /* Should be enough for 2^64 elements */
复制代码

查找过程:

按照图中所示,我们现在需要查找的是值为 7 的这个节点。步骤如下:

  • 从 head 节点开始,为了演示方便,这里显示的是4层,实际上的是64层。先是降一层到值 4 这个节点的这一层。如果不是所需要的值,那么就再降一层,跳跃到值为 6 的这一层。最后查找到值为 7 。这就是查找的过程,时间复杂度为 O(lg(n))

插入过程:

插入的过程和查找的过程类似:比如要插入的值为 6

  • 从 head 节点开始,先是在 head 开始降层来查找到最后一个比 6 小的节点,等到查到最后一个比 6 小的节点的时候(假设为 5 )。然后需要引入一个 随机层数算法 来为这个节点随机地建立层数。把这个节点插入进去以后,同时更新一遍最高的层数即可。

随机算法

/* Returns a random level for the new skiplist node we are going to create.
 * The return value of this function is between 1 and ZSKIPLIST_MAXLEVEL
 * (both inclusive), with a powerlaw-alike distribution where higher
 * levels are less likely to be returned. */
int zslRandomLevel(void) {
    int level = 1;
    while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
        level += 1;
    return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}
复制代码

Redis 源码中的晋升概率为25%,所以相对来说,Redis 的层高数相对来说是比较扁平化,层高相对较低,所以需要遍历的节点数量会多一些。

删除过程:

  • 删除的过程也是和查找的过程一样,先是找到要删除的那个值,再把这个值给删除,同时把重排一下指针和更新最高的层数。

更新过程:

  • 更新的过程和插入的过程都是是使用着 zadd 方法的,先是判断这个 value 是否存在,如果存在就是更新的过程,如果不存在就是插入过程。在更新的过程是,如果找到了Value,先删除掉,再新增,这样的弊端是会做两次的搜索,在性能上来讲就比较慢了,在 Redis 5.0 版本中, Redis 的作者 Antirez 优化了这个更新的过程,目前的更新过程是如果判断这个 value 是否存在,如果存在的话就直接更新,然后再调整整个跳跃表的 score 排序,这样就不需要两次的搜索过程。 可以看看关于 Antirez 这次的 更新优化代码。

实际的业务场景

Zset 数据结构

Redis中的强大的数据结构跳跃表(skiplist)的内部详解及实际运用
  • 如图所示,Zset 的数据结构是有一个 hash 表和一个跳跃表来结合的,hash 表上存储的是关于 String 的值和 Score 的值,跳跃表是用来辅助 hash 表来实现关于按照 score 来 排序 的功能。

所以跳跃表的实际运用场景就是 Zset 的实际运用场景

Zset的使用示例

//给某个集合增加权重和成员
//成员不可以为重复,权重可以重复,一个集合可以容纳到2^32-1个元素
//增加元素
redis 127.0.0.1:6379> ZADD spacedong 1 redis
redis 127.0.0.1:6379> ZADD spacedong 2 mongodb
redis 127.0.0.1:6379> ZADD spacedong 3 mysql

//获取集合中的元素个数
redis 127.0.0.1:6379> ZCARD spacedong 
"3"

//获取集合中的某个范围的成员
redis 127.0.0.1:6379> ZRANGE spacedong 0 2
1)  "redis"
2)  "mongodb"
3)  "mysql"
复制代码

Zset的实际运用场景

  • 在 Zset 中使用最多的场景就是涉及到排行榜类似的场景。例如实时统计一个关于分数的排行榜,这个时候可以使用 Redis 中的这个 ZSET 数据结构来维护。
  • 涉及到需要按照时间的顺序来排行的业务场景,例如如果需要维护一个问题池,按照时间的先后顺序来维护,这个时候也可以使用 Zset ,把时间当做权重,把问题当做 key 值来进行存取。

参考资料:


以上所述就是小编给大家介绍的《Redis中的强大的数据结构跳跃表(skiplist)的内部详解及实际运用》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Spring揭秘

Spring揭秘

王福强 / 人民邮电出版社 / 2009.8 / 99.00元

没有教程似的训导,更多的是说故事般的娓娓道来,本书是作者在多年的工作中积累的第一手Spring框架使用经验的总结,深入剖析了Spring框架各个模块的功能、出现的背景、设计理念和设计原理,揭开了Spring框架的神秘面纱,使你“知其然,更知其所以然”。每部分的扩展篇帮助读者活学活用Spring框架的方方面面,同时可以触类旁通,衍生出新的思路和解决方案。 本书内容全面,论述深刻入理,必将成为每......一起来看看 《Spring揭秘》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具