Redis设计与实现之跳跃表

目录

一、跳跃表

1、跳跃表的实现

2、跳跃表的应用

3、跳跃表的时间复杂度是什么?

二、跳跃表有哪些应用场景?

三、跳跃表和其他数据结构(如数组、链表等)相比有什么优点和缺点?

四、Redis的跳跃表支持并发操作吗?如何保证并发安全?

五、跳跃表的大小和内存占用是如何计算的?

六、Redis的跳跃表有没有限制元素数量的上限?

七、跳跃表的迭代器是如何实现的?

八、Redis的跳跃表和其他数据库(如MySQL、MongoDB等)的索引结构有何不同?

九、小结


一、跳跃表

跳跃表(skiplist)是一种随机化的数据,由 William Pugh 在论文《Skip lists: a probabilistic alternative to balanced trees》中提出,这种数据结构以有序的方式在层次化的链表中保存元 素,它的效率可以和平衡树媲美——查找、删除、添加等操作都可以在对数期望时间下完成, 并且比起平衡树来说,跳跃表的实现要简单直观得多。

以下是一个典型的跳跃表例子:

从图中可以看到,跳跃表主要由以下部分构成: 

•表头( head) :负责维护跳跃表的节点指针。

•跳跃表节点:保存着元素值,以及多个层。

•层:保存着指向其他元素的指针。高层的指针越过的元素数量大于等于低层的指针,为了提高查找的效率,程序总是从高层先开始访问,然后随着元素值范围的缩小,慢慢降低层次。

•表尾:全部由 NULL组成,表示跳跃表的末尾。

因为跳跃表的定义可以在任何一本算法或数据结构的书中找到,所以本章不介绍跳跃表的具体

实现方式或者具体的算法,而只介绍跳跃表在 Redis的应用、核心数据结构和 API。

1、跳跃表的实现

为了适应自身的功能需要, Redis基于 William Pugh 论文中描述的跳跃表进行了以下修改:

1.允许重复的 score值:多个不同的 member的score值可以相同。

2.进行对比操作时,不仅要检查 score值,还要检查 member:当score值可以重复时,单靠score值无法判断一个元素的身份,所以需要连 member域都一并检查才行。

3.每个节点都带有一个高度为 1层的后退指针,用于从表尾方向向表头方向迭代:当执行ZREVRANGE 或 ZREVRANGEBYSCORE 这类以逆序处理有序集的命令时,就会用到这个属性。

这个修改版的跳跃表由 redis.h/zskiplist 结构定义:

typedef structzskiplist {

    //头节点,尾节点

    structzskiplistNode *header, *tail;

    //节点数量

    unsigned longlength;

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

    intlevel;

} zskiplist;

跳跃表的节点由 redis.h/zskiplistNode 定义:

typedef structzskiplistNode {

    // member 对象

    robj*obj;
    
    //分值

    doublescore;
    
    //后退指针

    structzskiplistNode *backward;

    //层

    structzskiplistLevel {

        //前进指针

        structzskiplistNode *forward;

        //这个层跨越的节点数量

        unsigned intspan;

    } level[];

} zskiplistNode;

以下是操作这两个数据结构的 API,它们的作用以及相应的算法复杂度:

2、跳跃表的应用

和字典、链表或者字符串这几种在 Redis中大量使用的数据结构不同,跳跃表在 Redis的唯一作用,就是实现有序集数据类型。

跳跃表将指向有序集的 score值和member域的指针作为元素,并以 score值为索引,对有序集元素进行排序。

举个例子,以下代码就创建了一个带有 3个元素的有序集:

redis>ZADD s6x10y15z

(integer) 3

redis>ZRANGE s 0-1WITHSCORES

1)"x"

2)"6"

3)"y"

4)"10"

5)"z"

6)"15"

在底层实现中, Redis为x、y和z三个member分别创建了三个字符串,并为 6、10和15分别创建三个 double类型的值,然后用一个跳跃表将这些指针有序地保存起来,形成这样一个跳跃表:

为了展示的方便,在图片中我们直接将 member和score值包含在表节点中,但是在实际的定义中,因为跳跃表要和另一个实现有序集的结构(字典)分享 member和score值,所以跳跃表只保存指向 member和score的指针。

3、跳跃表的时间复杂度是什么?

跳跃表(Skip List)是一种数据结构,用于实现有序的集合数据。对于搜索、插入和删除操作,跳跃表的时间复杂度为O(log n),其中n是跳跃表中元素的数量。这是因为跳跃表通过层级索引的方式加速搜索的速度,使得每次搜索都能减少一半的搜索范围,从而使得时间复杂度保持在O(log n)。

二、跳跃表有哪些应用场景?

Redis跳跃表(Skip List)是一种有序数据结构,它通过使用多级索引来加快对元素的查找速度。它在Redis中的应用场景有:

  1. 有序集合:Redis的有序集合数据类型就是使用跳跃表来实现的。跳跃表能够保持集合元素的有序性,并且支持高效地插入、删除和查找操作。

  2. 排行榜:跳跃表可以用于实现排行榜功能,可以根据某个指标对用户进行排名,并且支持快速的插入和删除操作。

  3. 范围查询:跳跃表可以快速地查找某个范围内的元素,例如在一个有序集合中查找某个分数范围内的成员。

  4. 分数计算:跳跃表可以用于对成绩、评分等进行计算和排名。在Redis中,有序集合的元素可以关联一个分数,可以通过跳跃表来快速进行分数计算和排名。

  5. 事件排序:跳跃表可以用于对时间事件进行排序和管理,例如定时任务管理器、事件调度器等。

总的来说,Redis跳跃表适用于需要高效地进行元素排序、查找和范围查询的场景。它的优势在于简单、高效,适用于有序集合等需要有序性的场景。

三、跳跃表和其他数据结构(如数组、链表等)相比有什么优点和缺点?

Redis跳跃表相对于其他数据结构(如数组、链表等)具有以下优点和缺点:

优点:

  1. 快速查找:跳跃表通过索引层级的方式实现了快速查找,其时间复杂度为O(logn),比数组和链表的线性查找效率更高。
  2. 低延迟:由于跳跃表的查找和插入操作的时间复杂度都是O(logn),不随数据量的增长而增加,因此能够保持低延迟的性能。
  3. 支持有序集合:跳跃表能够自然有序地存储数据,因此非常适合实现有序集合等需要有序存储的数据结构。
  4. 空间效率高:跳跃表的索引层级结构为每个节点增加了额外的空间开销,但相对于数组和链表来说,其空间利用率更高,可以节省存储空间。

缺点:

  1. 复杂实现:相对于数组和链表等简单的数据结构来说,跳跃表的实现较为复杂,需要维护索引层级结构,并且需要考虑各节点之间的维护和调整操作。
  2. 内存占用:跳跃表的索引层级结构需要额外的空间来存储索引节点,因此会占用较多的内存空间。
  3. 不适合频繁的插入和删除操作:跳跃表的插入和删除操作需要对索引层级进行调整和维护,对于频繁进行这些操作的场景来说,性能可能较差。

四、Redis的跳跃表支持并发操作吗?如何保证并发安全?

Redis的跳跃表(Skip List)本身并不支持并发操作,因为跳跃表的插入、删除和查找操作都需要涉及到修改指针的过程,这样会存在并发安全问题。

为了保证并发安全性,Redis使用了一种叫做"多个跳跃表"的数据结构来实现有序集合(Sorted Set)。在每个有序集合中,Redis维护了一个正常的有序跳跃表,同时还维护了一个用于支持并发的层级跳跃表。

  • 正常的有序跳跃表:用于执行读操作,每个线程可以拥有自己的迭代器去遍历跳跃表。

  • 层级跳跃表:用于执行写操作,避免并发写入过程中对同一个节点进行修改。在更新节点时,首先会在层级跳跃表上进行更新,然后再更新到正常的有序跳跃表上。

通过这种方式,Redis实现了有序集合的并发操作,保证了并发安全性。

五、跳跃表的大小和内存占用是如何计算的?

Redis跳跃表(Skip List)是一种有序数据结构,用于实现有序集合键(Sorted Set Key)。跳跃表通过将数据分层,从而实现快速查找、插入和删除操作。

Redis跳跃表的大小取决于其中存储的键值对数量。每个节点包含一个或多个层级,每个层级维护了指向下一层级的指针。节点的数量和层级的大小都会影响跳跃表的大小。具体而言,跳跃表的大小可以通过以下公式计算:

Size = N * (entry size) + M * (pointer size)

其中,N表示跳跃表中的节点数量,entry size表示每个节点的键值对大小,M表示指针的数量,pointer size表示每个指针的大小。

跳跃表的内存占用主要取决于两个因素:节点数量和层级大小。节点数量较多会占用更多内存,而层级大小决定了每个节点的指针数量。因为指针通常比键值对更小,所以较大的层级大小可以节省内存。然而,较大的层级大小也会增加跳跃表的高度,可能增加查找操作的时间复杂度。

需要注意的是,Redis在使用跳跃表之前会使用普通的链表实现有序集合键。当数据量较小时,Redis会使用链表,当数据量超过一定阈值时,才会转换为跳跃表。因此,实际内存占用可能受到数据量和跳跃表与链表使用比例的影响。

六、Redis的跳跃表有没有限制元素数量的上限?

Redis的跳跃表(Skip List)没有固定的元素数量上限限制。每个跳跃表节点都可以包含多个元素,而每个节点的高度也是动态调整的,因此跳跃表可以根据需要动态地增长和缩小。跳跃表的高度可以通过调整概率来控制,从而控制跳跃表的大小。因此,Redis的跳跃表可以容纳非常大数量的元素。

七、跳跃表的迭代器是如何实现的?

Redis中的跳跃表(skiplist)是一种有序的数据结构,它通过多级索引来加速查询操作的效率。跳跃表的迭代器用于遍历跳跃表中的元素。

跳跃表的迭代器是通过指针和索引操作实现的。迭代器是一个可迭代对象,它在每次调用next()方法时返回下一个元素。迭代器可以在跳跃表中向前或向后遍历元素。

具体实现步骤如下:

  1. 创建一个迭代器对象,并初始化为跳跃表的头节点(或尾节点)。
  2. 在迭代器对象中,有一个指针指向当前节点。通过这个指针,可以访问当前节点的元素和索引。
  3. 通过指针和索引操作,可以实现在跳跃表中向前或向后遍历。例如,通过指针可以直接访问当前节点的下一个节点,从而实现向前或向后遍历。
  4. 在每次调用next()方法时,迭代器将返回当前节点的元素,并将指针指向下一个节点。
  5. 如果跳跃表中没有下一个节点,则迭代器将返回结束标记,表示遍历已经完成。

需要注意的是,当跳跃表中的元素被删除或添加时,迭代器可能会失效。为了保证迭代器的正确性,可以使用版本号或者加锁机制。

总而言之,Redis中的跳跃表迭代器通过指针和索引操作来遍历跳跃表中的元素,可以向前或向后遍历,并在每次调用next()方法时返回当前节点的元素。

八、Redis的跳跃表和其他数据库(如MySQL、MongoDB等)的索引结构有何不同?

Redis的跳跃表(skiplist)是一种有序数据结构,用于实现有序集合(Sorted Set)的索引。它通过多层级的有序链表来快速查找元素,具有类似于平衡二叉树的效率。

与其他数据库的索引结构相比,Redis的跳跃表有以下特点:

  1. 简单高效:跳跃表的实现相对简单,插入、删除和查找操作的时间复杂度都是O(log N),并且具有较低的常数系数。相比之下,其他数据库的索引结构如B树或哈希索引的复杂性更高。

  2. 内存友好:跳跃表存储在内存中,不需要进行磁盘读写操作,因此具有更低的延迟和更高的吞吐量。而其他数据库的索引结构如B树则需要频繁地读写磁盘。

  3. 有序性:跳跃表可以保持元素的有序性,并支持范围查询操作,如获取区间内的元素。

相比之下,其他数据库的索引结构如B树或哈希索引可能不具备这些特点。例如,B树需要进行平衡操作以维持树的平衡性,而哈希索引则无法提供范围查询操作。因此,Redis的跳跃表在某些场景下可以更加高效和灵活地满足需求。

九、小结

•跳跃表是一种随机化数据结构,它的查找、添加、删除操作都可以在对数期望时间下完成。

•跳跃表目前在 Redis的唯一作用就是作为有序集类型的底层数据结构(之一,另一个构

成有序集的结构是字典) 。

•为了适应自身的需求, Redis基于 William Pugh 论文中描述的跳跃表进行了修改,包括:

1.score值可重复。

2.对比一个元素需要同时检查它的 score和memeber 。

3.每个节点带有高度为 1层的后退指针,用于从表尾方向向表头方向迭代。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/248783.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

消除非受检警告

在Java中,有一些情况下编译器会生成非受检警告(Unchecked Warnings)。这些警告通常与泛型、类型转换或原始类型相关。消除这些警告可以提高代码的可读性和安全性。以下是一些常见的非受检警告以及如何消除它们的例子: 1. 泛型类型…

0基础学习VR全景平台篇第128篇:720VR全景拍摄设备推荐

上课!全体起立~ 大家好,欢迎观看蛙色官方系列全景摄影课程! 本篇教程,小编为大家推荐720VR全景拍摄所需要的设备器材。上节我们提到,理论上任意相机和镜头都能够拍摄全景,但为了标准化制作流程&#xff0…

oracle 锁表解决办法

相关表介绍 V$LOCKED_OBJECT(记录锁信息的表)v$session(记录会话信息的表)v$sql(记录 sql 执行的表)dba_objects(用来管理对象,表、库等等) 查询锁表的 SID select b.…

t-io 程序执行后,jvm不退出的原因

基于t-io 1.7.3 版本分析源码 1、设定当前时间,每10毫秒执行一次 (非守护线程) 2、对应线程池的核心线程在AioServer启动时全部激活,并且添加空任务到阻塞队列,让核心线程(非守护线程)一直存活

等等Domino 14.0FP1

大家好,才是真的好。 节奏确实太快了,有时候我深感我也追不上。 以前Notes Domino是三年磨一剑,也就说每三年才发一个大版本,从2019年开始,进行了高频提速,居然一年一个大版本! 周末&#xf…

使用opencv的Sobel算子实现图像边缘检测

1 边缘检测介绍 图像边缘检测技术是图像处理和计算机视觉等领域最基本的问题,也是经典的技术难题之一。如何快速、精确地提取图像边缘信息,一直是国内外的研究热点,同时边缘的检测也是图像处理中的一个难题。早期的经典算法包括边缘算子方法…

Linux--Docker容器(1)

这里写目录标题 简介名词解释作用 指令在本地创建容器的过程:(这里以tomcat为例)访问容器端口映射目录挂载验证端口映射验证目录挂载 删除镜像多小组访问容器mysql容器 简介 名词解释 Docker镜像:可以将镜像理解为面向对象的类&a…

黑豹程序员-axios+springmvc传递数组

问题 奇怪的现象,axios在往后台传递数组时,springmvc竟然接收不到 解决 尝试多次无果,突然看一篇文章写vue中的数组不是真正的数组需要强转转化JSON.stringify 将信将疑下测试了一把,还真的传递成功了。 不光要JSON.stringify…

[PyTorch][chapter 6][李宏毅深度学习][Logistic Regression]

前言: logistic回归又称logistic回归分析,是一种广义的线性回归分析模型,常用于数据挖掘,疾病自动诊断,经济预测等领域。 逻辑回归根据给定的自变量数据集来估计事件的发生概率,由于结果是一个概率&#xf…

linux内核使用ppm图片开机

什么是ppm图片 PPM(Portable Pixmap)是一种用于存储图像的文件格式。PPM图像文件以二进制或ASCII文本形式存储,并且是一种简单的、可移植的图像格式。PPM格式最初由Jef Poskanzer于1986年创建,并经过了多次扩展和修改。 PPM图像…

Axure的动态面板的使用

目录 1.什么是动态面板? 2.使用动态面板 ​编辑 轮播图 erp的登录系统 erp侧边栏 1.什么是动态面板? 动态面板是Axure的高级交互元件,由不同的状态面板组成,是我们制作交互过程中运用频率最高的元件,很多交互效果需…

【Docker】实战:nginx、redis

▒ 目录 ▒ 🛫 导读开发环境 1️⃣ Nginx 拉取 Nginx 镜像nginx.conf启动 Nginx访问 Nginx 2️⃣ redis拉取 Redis 镜像启动 Redis 容器测试 Redis 📖 参考资料 🛫 导读 开发环境 版本号描述文章日期2023-12-15操作系统Win10 - 22H222621.2…

网站服务器/域名/备案到底有什么关联?

​  在一个网站的组成中,网站服务器、域名、备案这几个要素是要被常提到的。在谈及三者关联之前,我们先了解下三者的各自概念。 域名:它是网站的唯一标识符,通俗理解来说就是用户在浏览器地址栏中输入的网址。一般来说&#xff…

MySQL作为服务端的配置过程与实际案例

MySQL是一款流行的关系型数据库管理系统,广泛应用于各种业务场景中。作为服务端,MySQL的配置过程对于数据库的性能、安全性和稳定性至关重要。本文将详细介绍MySQL作为服务端的配置过程,并通过一个实际案例进行举例说明。 一、MySQL服务端配…

新手HTML和CSS的常见知识点

​​​​ 目录 1.HTML标题标签(到)用于定义网页中的标题,并按照重要性递减排列。例如: 2.HTML段落标签()用于定义网页中的段落。例如: 3.HTML链接标签()用于创建链接…

首次使用 git 配置 github,gitee 密钥

gitee 和 github 密钥配置 1. 检查配置信息 使用命令 git config --global --list 检查邮箱是否一致 不一致可以使用如下命令进行设置 git config --global user.name "name" git config --global user.email "emailqq.com" 2. 生成 SSH 密钥 # 为 G…

2023年19款最佳3D打印软件

https://wenku.baidu.com/view/c0551497cf7931b765ce0508763231126edb77e3.html 2023年19款最佳3D打印软件 有免费也有付费!十款入门至专业级的3D打印软件推荐 【云图创智】23种最好用的3D打印软件,常用的3D打印软件都在这里了

HTML基础标签

但实际上无论声明为中文还是英文都可以写&#xff0c;中文/英文 主要是浏览器在进行调用翻译功能的时候&#xff0c;会按照声明的语言来进行翻译。 标签语义&#xff1a; 标签的属性一般都是在第一个标签中定义该标签效果所拥有的属性。 即标签的作用是什么 <>标签功能…

LeetCode(61)删除链表的倒数第 N 个结点【链表】【中等】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 删除链表的倒数第 N 个结点 1.题目 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5]示例…

关于“Python”的核心知识点整理大全22

目录 ​编辑 9.4.2 在一个模块中存储多个类 虽然同一个模块中的类之间应存在某种相关性&#xff0c;但可根据需要在一个模块中存储任意数量的 类。类Battery和ElectricCar都可帮助模拟汽车&#xff0c;因此下面将它们都加入模块car.py中&#xff1a; car.py my_electric_car…