Redis跳跃表的基本原理和实现

 更新时间:2022年06月09日 10:55:48   作者:溪午闻璐  
本文主要介绍了Redis跳跃表的基本原理和实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

一、概述

跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的

如下,对于单个链表来讲,即便链表中存储的数据是有序的,如果我们要向在其中查找某个数据,它只能从头到尾遍历链表。这样查找效率就会很低,时间复杂度会很高,达到了O(n)。

如果我们想要提高其查询效率,可以考虑在链表上构建索引的 方式,每两个节点提取一个节点到上级,我们把抽出来的那一级就叫做索引,如下:

此时,我们假设要查找节点8,我们可以先在索引层遍历,当遍历到索引层中值为7的节点时,发现下一个节点是9,那么要查找的节点肯定在这两个节点之间,我们下降到链表层继续遍历就找到了8这个节点。原来我们在单链表中找到8这个节点要遍历8个节点,而现在有了一级索引后,只需要遍历5个节点。

从上个例子中,我们可以看出,加来一层索引后,查找一个节点需要遍历的节点个数减少了,也就是说查询效率得到了提升,同理我们在一级索引的基础上,在加二级索引。

从图中我们可以看出,查找效率又有了提升,因为在这里例子中我们的数据量很少,当有大量的数据时,我们可以增加多级索引,在查询时,效率可以得到明显的提升。像这种链表增加多种索引的结构,就是跳跃表

Redis使用跳跃表作为有序集合键的底层实现之一,如果一个有序集合包含的元素数量比较多,又或者有序集合中元素的成员(member)是比较长的字符串时,Redis就会使用跳跃表来作为有序集合键的底层实现。

二、跳跃表的实现

Redis的跳跃表由zskiplistNodezskiplist两个结构定义,其中zskiplistNode结构用于表示跳跃表节点,而zskiplist结构则用于保存跳跃表节点的相关信息,比如节点的数量,以及指向表头节点和表尾节点的指针等等,如下,是一个跳跃表的结构:

上图片最左边的是zskiplist结构,该结构包含以下属性: 

  • header:指向跳跃表的表头节点,通过这个指针程序定位表头节点的时间复杂度就为O(1);
  • tail:指向跳跃表的表尾节点,通过这个指针程序定位表尾节点的时间复杂度就为O(1);
  • level:记录目前跳跃表内,层数最大的那个节点的层数(表头节点的层数不计算在内);通过这个属性可以再O(1)的时间复杂度内获取层高最高的节点的层数
  • length:记录跳跃表的长度,也即是,跳跃表目前包含节点的数量(表头节点不计算在内)通过这个属性,程序可以再O(1)的时间复杂度内返回跳跃表的长度

上图位于zskiplist结构右方的是四个zskiplistNode结构,该结构包含以下属性: 

  • 层(level):节点中用L1、L2、L3等字样标记节点的各个层,L1代表第一层,L2代表第二层,以此类推。每个层都带有两个属性:前进指针和跨度。前进指针用于访问位于表尾方向的其他节点,而跨度则记录了前进指针所指向节点和当前节点的距离(跨度越大,距离越远)。在上面的图片中,连线上带有数字的箭头就代表前进指针,而那个数字就是跨度。当程序从表头向表尾进行遍历时,访问会沿着层的前进指针进行。
  • 后退(backward)指针:节点中用BW字样标记节点的后退指针,它指向位于当前节点的前一个节点。后退指针在程序从表尾向表头遍历时使用
  • 分值(score):各个节点中的1.0、2.0和3.0是节点所保存的分值。在跳跃表中,节点按各自所保存的分值从小到大排列。
  • 成员对象(obj):各个节点中的o1、o2和o3是节点所保存的成员对象

2.1 跳跃表节点的zskiplisNode结构定义

typedef struct zskiplistNode {
    // 层
    struct zskiplistLevel {
        // 前进指针
        struct zskiplistNode *forward;
        // 跨度
        unsigned int span;
    } level[];
    // 后退指针
    struct zskiplistNode *backward;
    // 分值
    double score;
    // 成员对象
    robj *obj;
} zskiplistNode;

层:跳跃表节点的level数组可以包含多个元素,每个元素都包含一个指向其他节点的指针,程序可以通过这些层来加快访问其他节点的速度,一般来说,层的数量越多,访问其他节点的速度就越快。
每个层都有一个指向表尾方向的前进指针(level[i].forward属性),用于从表头向表尾方向访问节点
层的跨度(level[i].span属性)用于记录两个节点之间的距离。
节点的后退指针(backward属性)用于从表尾向表头方向访问节点:跟可以一次跳过多个节点的前进指针不同,因为每个节点只有一个后退指针,所以每次只能后退至前一个节点
节点的分值(score属性)是一个double类型的浮点数,跳跃表中的所有节点都按分值从小到大来排序;
节点的成员对象(obj属性)是一个指针,它指向一个字符串对象,而字符串对象则保存着一个SDS值

2.2 zskiplist结构的定义

typedef struct zskiplist {
    // 表头节点和表尾节点
    structz skiplistNode *header, *tail;
    // 表中节点的数量
    unsigned long length;
    // 表中层数最大的节点的层数
    int level;
} zskiplist;

header和tail指针分别指向跳跃表的表头和表尾节点,通过这两个指针,程序定位表头节点和表尾节点的复杂度为O(1)。

通过使用length属性来记录节点的数量,程序可以在O(1)复杂度内返回跳跃表的长度。

三、结束

Redis使用跳跃表作为有序集合键的底层实现之一,如果一个有序集合包含的元素数量比较多,又或者有序集合中元素的成员(member)是比较长的字符串时,Redis就会使用跳跃表来作为有序集合键的底层实现。之所以如此,是因为跳跃表在链表的基础上增加了多级索引以提升查找的效率,但其是一个空间换时间的方案,必然会带来一个问题——索引是占内存的原始链表中存储的有可能是很大的对象,而索引结点只需要存储关键值值和几个指针,并不需要存储对象,因此当节点本身比较大或者元素数量比较多的时候,其优势必然会被放大,而缺点则可以忽略

到此这篇关于Redis跳跃表的基本原理和实现的文章就介绍到这了,更多相关Redis跳跃表内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Redis配置文件redis.conf详细配置说明

    Redis配置文件redis.conf详细配置说明

    本文列出了Redis的配置文件redis.conf的各配置项的详细说明,简单易懂
    2018-03-03
  • 压缩Redis里的字符串大对象操作

    压缩Redis里的字符串大对象操作

    这篇文章主要介绍了压缩Redis里的字符串大对象操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • redis实现加锁的几种方法示例详解

    redis实现加锁的几种方法示例详解

    这篇文章主要给大家介绍了关于redis实现加锁的几种方法,加锁命令分别是INCR、SETNX和SET,文中给出了详细的示例代码,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-09-09
  • Redis高效检索地理位置的原理解析

    Redis高效检索地理位置的原理解析

    这篇文章主要介绍了Redis是如何高效检索地理位置,通过geo相关的命令,可以很容易在redis中存储和使用经纬度坐标信息,具体实现方法跟随小编一起看看吧
    2021-06-06
  • redis启动和退出命令行简单操作步骤

    redis启动和退出命令行简单操作步骤

    Redis是一种键值存储数据库,用户可以使用它来存储和检索大量的键值数据,下面这篇文章主要给大家介绍了关于redis启动和退出命令行的相关资料,需要的朋友可以参考下
    2024-03-03
  • 一文详解Redis为什么一定要设置密码原理

    一文详解Redis为什么一定要设置密码原理

    这篇文章主要为大家介绍了Redis为什么一定要设置密码原理详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03
  • 关于Redis的内存淘汰策略详解

    关于Redis的内存淘汰策略详解

    当内存空间使用达到限制时,Redis 会根据配置策略来选择不同处理方式,要么返回 errors,要么按照不同的策略算法来清除一些旧数据,达到回收内存的目的,这就是 Redis 的内存淘汰,有些文章中,内存淘汰也叫缓存回收,需要的朋友可以参考下
    2023-05-05
  • Redis全局ID生成器的实现

    Redis全局ID生成器的实现

    全局ID生成器,是一种在分布式系统下用来生成全局唯一ID的工具,本文主要介绍了Redis全局ID生成器的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06
  • Redis基本数据类型Set常用操作命令

    Redis基本数据类型Set常用操作命令

    这篇文章主要为大家介绍了Redis基本数据类型Set常用操作命令,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • Redis 单节点部署的实现

    Redis 单节点部署的实现

    本文主要介绍了Redis 单节点部署的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06

最新评论