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) 3redis>ZRANGE s 0-1WITHSCORES1)"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中的应用场景有:
-
有序集合:Redis的有序集合数据类型就是使用跳跃表来实现的。跳跃表能够保持集合元素的有序性,并且支持高效地插入、删除和查找操作。
-
排行榜:跳跃表可以用于实现排行榜功能,可以根据某个指标对用户进行排名,并且支持快速的插入和删除操作。
-
范围查询:跳跃表可以快速地查找某个范围内的元素,例如在一个有序集合中查找某个分数范围内的成员。
-
分数计算:跳跃表可以用于对成绩、评分等进行计算和排名。在Redis中,有序集合的元素可以关联一个分数,可以通过跳跃表来快速进行分数计算和排名。
-
事件排序:跳跃表可以用于对时间事件进行排序和管理,例如定时任务管理器、事件调度器等。
总的来说,Redis跳跃表适用于需要高效地进行元素排序、查找和范围查询的场景。它的优势在于简单、高效,适用于有序集合等需要有序性的场景。
三、跳跃表和其他数据结构(如数组、链表等)相比有什么优点和缺点?
Redis跳跃表相对于其他数据结构(如数组、链表等)具有以下优点和缺点:
优点:
- 快速查找:跳跃表通过索引层级的方式实现了快速查找,其时间复杂度为O(logn),比数组和链表的线性查找效率更高。
- 低延迟:由于跳跃表的查找和插入操作的时间复杂度都是O(logn),不随数据量的增长而增加,因此能够保持低延迟的性能。
- 支持有序集合:跳跃表能够自然有序地存储数据,因此非常适合实现有序集合等需要有序存储的数据结构。
- 空间效率高:跳跃表的索引层级结构为每个节点增加了额外的空间开销,但相对于数组和链表来说,其空间利用率更高,可以节省存储空间。
缺点:
- 复杂实现:相对于数组和链表等简单的数据结构来说,跳跃表的实现较为复杂,需要维护索引层级结构,并且需要考虑各节点之间的维护和调整操作。
- 内存占用:跳跃表的索引层级结构需要额外的空间来存储索引节点,因此会占用较多的内存空间。
- 不适合频繁的插入和删除操作:跳跃表的插入和删除操作需要对索引层级进行调整和维护,对于频繁进行这些操作的场景来说,性能可能较差。
四、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()方法时返回下一个元素。迭代器可以在跳跃表中向前或向后遍历元素。
具体实现步骤如下:
- 创建一个迭代器对象,并初始化为跳跃表的头节点(或尾节点)。
- 在迭代器对象中,有一个指针指向当前节点。通过这个指针,可以访问当前节点的元素和索引。
- 通过指针和索引操作,可以实现在跳跃表中向前或向后遍历。例如,通过指针可以直接访问当前节点的下一个节点,从而实现向前或向后遍历。
- 在每次调用next()方法时,迭代器将返回当前节点的元素,并将指针指向下一个节点。
- 如果跳跃表中没有下一个节点,则迭代器将返回结束标记,表示遍历已经完成。
需要注意的是,当跳跃表中的元素被删除或添加时,迭代器可能会失效。为了保证迭代器的正确性,可以使用版本号或者加锁机制。
总而言之,Redis中的跳跃表迭代器通过指针和索引操作来遍历跳跃表中的元素,可以向前或向后遍历,并在每次调用next()方法时返回当前节点的元素。
八、Redis的跳跃表和其他数据库(如MySQL、MongoDB等)的索引结构有何不同?
Redis的跳跃表(skiplist)是一种有序数据结构,用于实现有序集合(Sorted Set)的索引。它通过多层级的有序链表来快速查找元素,具有类似于平衡二叉树的效率。
与其他数据库的索引结构相比,Redis的跳跃表有以下特点:
-
简单高效:跳跃表的实现相对简单,插入、删除和查找操作的时间复杂度都是O(log N),并且具有较低的常数系数。相比之下,其他数据库的索引结构如B树或哈希索引的复杂性更高。
-
内存友好:跳跃表存储在内存中,不需要进行磁盘读写操作,因此具有更低的延迟和更高的吞吐量。而其他数据库的索引结构如B树则需要频繁地读写磁盘。
-
有序性:跳跃表可以保持元素的有序性,并支持范围查询操作,如获取区间内的元素。
相比之下,其他数据库的索引结构如B树或哈希索引可能不具备这些特点。例如,B树需要进行平衡操作以维持树的平衡性,而哈希索引则无法提供范围查询操作。因此,Redis的跳跃表在某些场景下可以更加高效和灵活地满足需求。
九、小结
•跳跃表是一种随机化数据结构,它的查找、添加、删除操作都可以在对数期望时间下完成。
•跳跃表目前在 Redis的唯一作用就是作为有序集类型的底层数据结构(之一,另一个构
成有序集的结构是字典) 。
•为了适应自身的需求, Redis基于 William Pugh 论文中描述的跳跃表进行了修改,包括:
1.score值可重复。
2.对比一个元素需要同时检查它的 score和memeber 。
3.每个节点带有高度为 1层的后退指针,用于从表尾方向向表头方向迭代。
相关文章:
Redis设计与实现之跳跃表
目录 一、跳跃表 1、跳跃表的实现 2、跳跃表的应用 3、跳跃表的时间复杂度是什么? 二、跳跃表有哪些应用场景? 三、跳跃表和其他数据结构(如数组、链表等)相比有什么优点和缺点? 四、Redis的跳跃表支持并发操作吗…...
[每周一更]-(第27期):HTTP压测工具之wrk
[补充完善往期内容] wrk是一款简单的HTTP压测工具,托管在Github上,https://github.com/wg/wrkwrk 的一个很好的特性就是能用很少的线程压出很大的并发量. 原因是它使用了一些操作系统特定的高性能 io 机制, 比如 select, epoll, kqueue 等. 其实它是复用了 redis 的 ae 异步事…...
【FunASR】Paraformer语音识别-中文-通用-16k-离线-large-onnx
模型亮点 模型文件: damo/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-pytorchParaformer-large长音频模型集成VAD、ASR、标点与时间戳功能,可直接对时长为数小时音频进行识别,并输出带标点文字与时间戳: ASR模型…...
C语言中的柔性数组
uint8_t data[0];代码的含义老虎开始对这个数组不太了解,查阅后得知这是个柔性数组。 C语言中的柔性数组(Flexible Array Member)是一种特殊的数组,它被定义在结构体的最后一个元素中,其大小未知,也就是所…...
ca-certificates.crt解析加载到nssdb中
openssl crl2pkcs7 -nocrl -certfile /etc/ssl/certs/ca-certificates.crt | openssl pkcs7 -print_certs -noout -text ca-certificates.crt为操作系统根证书列表。 获取证书以后使用PK11_ImportDERCert将证书导入到nssdb中 base::FilePath cert_path base::FilePath("…...
聊聊Java中的常用类String
String、StringBuffer、StringBuilder 的区别 从可变性分析 String不可变。StringBuffer、StringBuilder都继承自AbstractStringBuilder ,两者的底层的数组value并没有使用private和final修饰,所以是可变的。 AbstractStringBuilder 源码如下所示 ab…...
R语言piecewiseSEM结构方程模型在生态环境领域实践技术
结构方程模型(Sructural Equation Modeling,SEM)可分析系统内变量间的相互关系,并通过图形化方式清晰展示系统中多变量因果关系网,具有强大的数据分析功能和广泛的适用性,是近年来生态、进化、环境、地学、…...
IDEA设置查看JDK源码
问题 我们在查看JDK源码时,可能会遇到这种情况,步入底层查看JDK源码时,出现一堆var变量,可读性非常之差,例如笔者最近想看到nio包下的SocketChannelImpl的write方法,结果看到这样一番景象: pu…...
SSM—Mybatis
目录 和其它持久化层技术对比 搭建MyBatis 开发环境 创建maven工程 创建MyBatis的核心配置文件 创建mapper接口 创建MyBatis的映射文件 通过junit测试功能 加入log4j日志功能 核心配置文件详解 MyBatis的增删改查 新增 删除 修改 查询一个实体类对象 查询list集…...
MYSQL在不删除数据的情况下,重置主键自增id
MYSQL在不删除数据的情况下,重置主键自增id 方法一: SET num : 0; UPDATE table_name SET id num : (num1); ALTER TABLE table_name AUTO_INCREMENT 1; 方法二: 背景(mysql 数据在进行多次删除新增之后id变得很大,但是并没…...
SpringMVC-servlet交互
servlet交互 1.1 引入servlet依赖 <dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version><scope>provided</scope></dependency>1.2 创建testservl…...
DICOM 文件中,VR,VL,SQ,图像二进制的几个注意点
DICOM 文件的结构,在网上有很多的学习资料,这里只介绍些容易混淆的概念,作为回看笔记。 1. 传输语法 每个传输语法,起都是表达的三个概念:大小端、显隐式、压缩算法 DICOM Implicit VR Little Endian: 1.2.840.1000…...
git 的使用
git reset详解-CSDN博客 git reset 命令详解 git revert命令详解。-CSDN博客 关于Git分支中HEAD和Master的理解 - 知乎 (zhihu.com) 一文带你精通 Git(Git 安装与使用、Git 命令精讲、项目的推送与克隆)-CSDN博客 Git 常用操作(5ÿ…...
详解—【C++】lambda表达式
目录 前言 一、lambda表达式 二、lambda表达式语法 2.1. lambda表达式各部分说明 2.2. 捕获列表说明 三、函数对象与lambda表达式 前言 在C98中,如果想要对一个数据集合中的元素进行排序,可以使用std::sort方法。 #include <algorithm> #i…...
Qt Desktop Widgets 控件绘图原理逐步分析拆解
Qt 是目前C语言首选的框架库。之所以称为框架库而不单单是GUI库,是因为Qt提供了远远超过GUI的功能封装,即使不使用GUI的后台服务,也可以用Qt大大提高跨平台的能力。 仅就界面来说,Qt 保持各个平台绘图等效果的统一,并…...
什么是rocketmq❓
在大规模分布式系统中,各个服务之间的通信是至关重要的,而RocketMQ作为一款分布式消息中间件,为解决这一问题提供了强大的解决方案。本文将深入探讨RocketMQ的基本概念、用途,以及在实际分布式系统中的作用,并对Produc…...
【网络安全】HTTP Slowloris攻击原理解析
文章目录 Slowloris攻击的概念Slowloris攻击原理Slowloris攻击的步骤其他的DDoS攻击类型UDP FloodICMP (Ping) FloodSYN FloodPing of DeathNTP AmplificationHTTP FloodZero-day DDoS 攻击 推荐阅读 Slowloris攻击的概念 Slowloris是在2009年由著名Web安全专家RSnake提出的一…...
从最近爆火的ChatGPT,我看到了电商的下一个形态
爆火的ChatGPT似乎让每个行业有了改造的可能性,电商行业也不例外。 在讨论了很多流量红利消失的话题后,我们看到互联网电商行业不再性感,从淘宝天猫,京东,到拼多多,再到抖音,快手,电…...
云原生向量计算引擎 PieCloudVector:为大模型提供独特记忆
拓数派大模型数据计算系统(PieDataComputingSystem,缩写:πDataCS)在10月24日程序员节「大模型数据计算系统」2023拓数派年度技术论坛正式发布。πDataCS 以云原生技术重构数据存储和计算,「一份存储,多引擎…...
大创项目推荐 深度学习 opencv python 实现中国交通标志识别
文章目录 0 前言1 yolov5实现中国交通标志检测2.算法原理2.1 算法简介2.2网络架构2.3 关键代码 3 数据集处理3.1 VOC格式介绍3.2 将中国交通标志检测数据集CCTSDB数据转换成VOC数据格式3.3 手动标注数据集 4 模型训练5 实现效果5.1 视频效果 6 最后 0 前言 🔥 优质…...
深度学习实战67-基于Stable-diffusion的图像生成应用模型的搭建,在Kaggle平台的搭建部署,解决本地没有算力资源问题
大家好,我是微学AI,今天给大家介绍一下深度学习实战67-基于Stable-diffusion的图像生成应用模型的搭建,在Kaggle平台的搭建部署,解决本地没有算力资源问题。稳定扩散模型(Stable Diffusion Model)是一种用于图像增强和去噪的计算机视觉算法。它通过对输入图像进行扩散过程…...
云原生之深入解析Kubernetes本地持久化存储方案OpenEBS LocalPV的最佳实践
一、K8s 本地存储 K8s 支持多达 20 种类型的持久化存储,如常见的 CephFS 、Glusterfs 等,不过这些大都是分布式存储,随着社区的发展,越来越多的用户期望将 K8s 集群中工作节点上挂载的数据盘利用起来,于是就有了 loca…...
设计模式-策略(Strategy)模式
又被称为政策(方针)模式策略模式(Strategy Design Pattern):封装可以互换的行为,并使用委托来决定要使用哪一个策略模式是一种行为设计模式,它能让你定义一系列算法,并将每种算法分别放入独立的类中&#x…...
Star 4.1k!Gitee GVP开源项目!新一代桌面应用开发框架 ElectronEgg!
前言 随着现代技术的快速升级迭代及发展,桌面应用开发已经变得越来越普及。然而对于非专业桌面应用开发工程师在面对这项任务时,可能会感到无从下手,甚至觉得这是一项困难的挑战。 本篇文章将分享一种新型桌面应用开发框架 ElectronEgg&…...
node.js学习(简单聊天室)
在掘金查看该文章 1. TCP服务搭建 1.1 socket 先来粗略了解下socket 套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中&am…...
cfa一级考生复习经验分享系列(四)
备考CFA一级满打满算用了一个多月,每天八个小时以上。可能如果仅以通过为目标的话完全不用这样,看过太多类似于只看了一周就通过了考试又或是放弃了好几门飘过了考试的情况,我觉得这是不正确的考试状态,完全不必惊叹,踏…...
PPT插件-好用的插件-放映笔、绘图板-大珩助手
放映笔 幻灯片放映时,工具在幻灯片的左下方,本工具在幻灯片的右侧,可以移动,可以方便在右侧讲课时候使用 绘图板 可在绘图板上写签名、绘制图画、写字等等,点画笔切换橡皮擦,点插入绘图,将背景…...
弧形导轨的安装注意事项
随着弧形导轨的应用日渐普遍,在日常使用中总会遇到很多各种各样的问题,原因很多是安装不正确或者使用不恰当。不合理的使用不但不能充分发挥其价值还会导致使用寿命大打折扣,使企业造成不必要的损失,因此大伙有必要了解一些安装的…...
Elasticsearch优化-04
Elasticsearch优化 1、优化-硬件选择 Elasticsearch 的基础是 Lucene,所有的索引和文档数据是存储在本地的磁盘中,具体的路径可在 ES 的配置文件…/config/elasticsearch.yml中配置,如下: # #Path to directory where to store …...
Springboot+vue的公寓报修管理系统(有报告)。Javaee项目,springboot vue前后端分离项目
演示视频: Springbootvue的公寓报修管理系统(有报告)。Javaee项目,springboot vue前后端分离项目 项目介绍: 本文设计了一个基于Springbootvue的前后端分离的公寓报修管理系统,采用M(model&…...
网站制作公司茂名/无限制访问国外的浏览器
最近打开了交接过来的旧代码,编译了一下,出现以下错误: Error[Li005]: no definition for "__disable_interrupt" Error[Li005]: no definition for "__enable_interrupt"解决方法:添加头文件#include <…...
无锡快速建设网站方法/百度网站推广一年多少钱
• 向指定的txt文件中写入键盘输入的内容,然后再重新读取该文件的内容,显示到控制台上。 • 键盘录入5个学生信息(姓名, 成绩),按照成绩从高到低存入文本文件。 package 复习第七章作业第四答题;import java.io.*; import java.util.*;publ…...
眉山网站建设/设计网站的软件
这是本系列第19篇文章,至此,Lightroom Classic的面板栏已经讲完了,今天来讲讲直方图与面板栏之间这一小条:工具栏。虽然这里有6个工具 ,但我都放在一篇文章里讲,大家可以快速了解这些工具。这些工具很多与C…...
wordpress 关键字插件/宁波网络推广优化方案
前言 Android Build 系统是 Android 源码的一部分。关于如何获取 Android 源码,请参照 Android Source 官方网站: http://source.android.com/source/downloading.html。 Android Build 系统用来编译 Android 系统,Android SDK 以及相关文档。…...
ssm框架做网站的优势/企业营销网站建设系统
本文实例为大家分享了Java实现寻找迷宫出路的具体代码,供大家参考,具体内容如下项目名称寻找迷宫出路项目描述给定一个自定义迷宫,0表示能通过,1表示不能通过。通过程序找出正确的迷宫出路,并将正确的路线改为2输出。代…...
公司网站建设价格/阿里云域名注册官网
前言 纵观神经网络的发展历程,从最原始的MLP,到CNN,到RNN,到LSTM,GRU,再到现在的Attention机制,人们不断的在网络里面加入一些先验知识,使得网络不过于“发散”,能够朝着人们希望的…...