数据结构-redis数据结构-跳表skiplist

2023-10-31

这篇文章简单分享学习redis(6.0)数据结构-跳表skiplist

redis中的有序数据集合[zset],有两种实现方式:跳表和压缩列表,我们今天学习下跳表的实现原理。

学习新的知识,我们先从已掌握的知识入手,由浅入深,让我们先从普通链表开始,如果希望一个集合有序,我们会想到通过有序链表实现:

我们在该单链表中查询元素[15],经过1,3,5,7,9,11,13,15,需要从头到尾遍历8次,效率很低。单链表的查询时间复杂度为O(N)。

如何提升查询效率呢?我们可以为链表建立“索引”:每两个元素提取一层建立索引:

然后我们在新的两层链表中查询元素[15],经过1,5,9,13,15,需要查询5次。通过建立索引达到了减少查询次数的目的。时间复杂度为O(logN)O(logN)。

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

下面我们分析下跳表的时间复杂度如何计算的:

假设我们严格的按照每两个元素提取一层建立索引,如上图的三层索引,N:元素的总规模,h:索引层高

第一层索引元素个数:N/2N/2,

第二层索引元素个数N/2*2N/2∗2,

第三层索引元素个数N/2*2*2N/2∗2∗2

我们可以得到 数据总规模N与索引层高h的函数关系,最底层的索引元素个数为2=N/2h2=N/2h,即可推出h = log2N - 1h=log2N−1,再加上最底层的原始链表 h = log2Nh=log2N (log以2为底N的对数)

跳表的时间复杂度 = 层高(h) * 每层遍历的个数(m),当数据规模很大的时候,我们可以忽略常数项m,所以链表的时间复杂度O(logN)O(logN)

我们上面的例子是为了说明跳表的基本结构,简化的模型,下面再举个更直观的例子,当数据量很大时,可以明显的看到极大的提高查询效率:

 比如查找9998,通过建立索引我们能减少很多的查询过程。



以上我们简单认识了跳表,今天的重点是分析redis中跳表的数据结构。

Redis 的跳跃表实现和 WillianmPugh [WillianmPugh博士在他的论文中提出跳表的数据结构]在 “ Skip Lists: A Probabilistic Alternative to Balanced Trees ” 中描述的跳跃表算法类似,但又有有些区别:

1、Redis 的跳跃表允许分值重复;
2、Redis 的跳跃表排序不止根据分值,在分值相同的时候会对比成员对象;
3、Redis 的跳跃表有一个后退指针,形成了一个双向链表,可以从表尾向表头遍历,用于 ZREVRANGE 命令的实现。

 
Redis的跳跃表由 zskiplistNode 和 skiplist 两个结构定义:

zskiplistNode结构用于表示跳跃表节点;

zskiplist结构则用于保存跳跃表节点的相关信息,比如节点的数量,以及指向表头节点和表尾节点的指针等

skiplis
header:指向跳跃表的表头节点。

tail:指向跳跃表的表尾节点。

ength:记录跳跃表的长度,即除去表头节点之外的节点数量之和。

level:记录跳跃表内层数最大的那个节点的层数(表头节点的层数不计算在内)。

zskiplistNode

ele:用于记录跳跃表节点的值,类型是 SDS。
 

score:保存跳跃表节点的分值,在跳跃表中,节点按各自所保存的分值从小到大排列。

backward:后退指针,它指向当前节点的前一个节点,在程序从表尾向表头遍历时使用。因为一个跳跃表节点只有一个后退指针,所以每次只能后退至前一个节点。
 

level:跳跃表节点的层,每个层都带有两个属性:前进指针 forward 和跨度 span。前进指针用于访问位于表尾方向的其他节点,而跨度则记录了前进指针所指向节点和当前节点的距离。每个跳跃表节点最多有 32 层。

zskiplistLevel包含以下两个属性:

forward:指向同一层的下一个节点,为节点的forward指向NULL 

span:forward指向的节点与本节点之间的节点的个数,span越大说明跳过的节点的个数越多

下面我们分析下跳表插入节点的过程,有助于进一步了解跳表的数据结构及实现原理:

这是第一步查询的要插入的位置的描述:

此处详细分析了查找插入节点的过程分析,因为新增、修改、删除节点都涉及查询


                
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构-redis数据结构-跳表skiplist 的相关文章

  • JSF2.0 中的空白输入字段未设置为 NULL

    我有一个支持 bean 其中 fileld 为 Long Double Integer String 当我没有在输入字段中指定任何内容时 长整型 整数和双精度值将被视为零 而不是空 我正在使用 tomcat 来部署我的应用程序 有什么解决办
  • 序列化 ArrayList

    我正在尝试编写一个 Android 游戏 即使用户想要返回主菜单或者活动被系统终止 我也希望能够暂停游戏 onSaveInstanceState 似乎并没有给我很大的控制权来决定何时可以读回捆绑包 而且据我所知 捆绑包仅在短时间内有效 所以
  • 合并 2 个 .jks 信任库文件

    我正在使用启用了 SSL 的 Tomcat 并使用信任库进行客户端身份验证 我有两个 jks trustore 文件 第一个 我将其用于 PROD 环境 另一个用于 TEST 环境客户端证书 我在 Tomcat 上部署了 Web 应用程序
  • Java 8 可选

    我想检查特定对象大小是否大于 0 如果它大于 0 那么我想创建一个可选对象 如果不是 那么我想返回一个可选的空对象 这是java代码的长版本 if fooA size gt 0 return Optional of new Foo else
  • Java 比 Xmx 参数消耗更多内存

    我有一个非常简单的 Web 服务器类 基于 Java SEHttpServer class 当我使用此命令启动编译的类来限制内存使用时 java Xmx5m Xss5m Xrs Xint Xbatch Test 现在如果我使用检查内存top
  • 如何从球衣服务端点发送实体列表?

    我正在从球衣服务器发送实体列表 在客户端 我试图获取这些实体列表 但它给了元帅例外 为什么它在元素名末尾添加 s 即 emps 而不是 emp XmlRootElement public class Emp Server side code
  • 内部/匿名类的最佳实践[关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 匿名类和静态内部类的最佳实践 设计和性能方面 是什么 就我个人而言 我认为静态内部类提供了更好的封装 并且应该提供更好的性能 因为它们无法访问类
  • 图像在 3D 空间中绕 Y 轴旋转

    我有一个 BufferedImage 我想用 theta 角而不是仿射变换绕 Java 中的 Y 轴旋转图像 图片 旋转将如下图所示 矩形将是图像 我可以通过旋转图像的每个像素并绘制图像来做到这一点 因为我必须旋转很多图像 所以我认为这不是
  • 如何确定 JDialog 显示在哪个屏幕上

    我有一个非常大的应用程序 有多个对话框 我的任务是确保不完全可见的对话框 因为用户将其从可见屏幕区域拉出 移回屏幕中心 当我只处理一个屏幕时 这没问题 它工作得很好 但是 该应用程序的大多数用户的桌面上都有两个屏幕 当我尝试找出对话框显示在
  • 如何在 Android 中签署 AAR Artifacts?

    我目前正在开发一个 AAR android 库 我想用我自己的密钥对已发布的工件进行签名 以便我可以确定我是否发布了具有相同名称和功能的假 aar 注意事项1 我希望能够以编程方式检查我的库的真实性 即使是一个伪造的库 只是伪造了我的 aa
  • TableModel setCellEditable 并自动将值设置回 false

    我目前正在尝试在 JTable 中实现 JPopupMenu 它允许解锁单元格以进行编辑 Override public void actionPerformed ActionEvent e if e getActionCommand Un
  • Android Fabric Crashlytics 崩溃,初始化时未找到资源

    我从 google play 控制台收到了这份报告 看起来 Fabric 在启动时崩溃了 因为某些用户出现了资源未找到的异常 java lang RuntimeException at android app ActivityThread
  • 如何在 SpringBoot v3.0.0 中使用嵌入式 MongoDB?

    我正在尝试连接嵌入式 mongodb 并使用 MongoDbSpringIntegrationTest 对其进行测试 问题是相同的代码在 2 7 7 中适用于 spring boot 但在 3 0 0 中不适用于 spring boot 问
  • Hibernate更新查询问题

    对于此更新查询 update TestDB dbo MyEmp set empname where empid 我在 DAO 课上写的 MyEmployee myEmployee new MyEmployee MyEmployee myEm
  • Android 的@hide 注解到底有什么作用?

    Android中很多内部API都被标记出来了 hide What exactly这是吗 另一个答案 https stackoverflow com questions 17035271 what does hide mean in the
  • 错误包括 bouncycastle 提供商

    我需要使用bouncycastle provider我的项目中的库 我已将其包含在 gradle 项目中 apply plugin application sourceCompatibility 1 6 version 1 0 0 main
  • 用二进制数、常规数字和格雷编码填充矩阵

    我有一个包含 1 s 或 0 s 的矩阵 用于创建二进制数 其宽度为n 对于 n 2 和 n 3 它看起来像 00 000 01 001 10 010 11 011 100 101 110 111 等等 现在我正在使用以下代码来生成它 in
  • 为什么我无法使用 HttpUrlConnection 上传第一个文件块?

    在我的项目中 我应该从一台服务器逐块下载文件 并将每个块立即上传到另一台服务器 我有一个应该下载的文件的 URL 我们就这样称呼它吧downloadUrl 因此 这就是我逐块下载文件的方式 val chunkSize 1024 1024 B
  • java.lang.IllegalStateException:FragmentManager 已被销毁

    活动中onResume我称之为 volley request 的方法 它获取项目列表 然后将它们加载到此活动内的 ListFragment 中 当我第一次进入活动时 一切正常 但当我重新进入活动时 ListFragment 为空 并且控制台
  • 使用 SimpleDateFormat、Java 进行错误的日期解析

    我需要使用日期模式 yyyy MM dd 解析输入字符串中的日期 如果日期采用任何其他格式 则抛出错误 这是我解析日期的代码 private void validateDate throws MyException Date parsedD

随机推荐

  • MySQL多表关系及多表查询

    多表关系 在关系型数据库中存在着三种多表关系 分别是一对多 多对一 多对多以及一对一 之所以会产生这些关系 是因为在进行数据设计的时候 分析得出业务之间存在着一定的关系 进而在数据中也就存在了这些关系 一对多 一对多是最基础的表间关系 意思
  • 给大家推荐一门比较适合用来学习流量分析技术的公开课(内附课程b站链接)

    最近看到同事在看一门课程 是CSNA出的一套免费对外公开课 跟着看了一下觉得不错 分享给大家 CSNA是国内比较老牌且低调的网络技术分析认证培训了 在网络运维和网络安全方向上还是有口皆碑的 课程内容比较体系化 有理论也有实操 涵盖了业务性能
  • php面试题猴子123报数(猴子选大王)

    题目就是有N个猴子 123循环报数数到3的猴子被踢出下一个接着报数 一遍一遍的循环直到剩余一个猴子 求这个猴子是最开始的第几号猴子 我想到了两个方法 第一个就是模拟报数的模式 每到3的时候unset一个元素 最后剩余的就是 要求的猴子 代码
  • windows如何关闭IIS (因为占用80端口而无法启动nginx)

    一 场景概述 正在写一个Web项目但是每次输入都需要加上端口号 所以想用服务器代理一下端口 让可以直接用nginx来解决这个问题但是 nginx异常无法打开 结果发现80端口被异常占用 因为windows的IIS也占用80端口号 比如win
  • 电脑命令教程计算机基础知识,电脑常用运行命令图文教程(DOS命令)

    本文介绍一些常用的运行窗口命令 也是DOS命令 同时所有的命令均在win7旗舰版测试通过 并附有运行后的图片 运行命令窗口如下 工具 原料 电脑一台 本文以win7系统的电脑为例 方法 步骤 1 调出运行命令窗口 按快捷键 win R 或者
  • java|8.18总结|基本功能

    1 思维导图 2 用自己的话描述某知识点是什么 举例 总结 一句话总结 环境变量 理解 环境变量相当于提前封装好一个环境 功能 在此环境下 比如在有path环境下 可以直接执行java文件 不用先进入JAVA的目录才能运行java 如果需要
  • 使用Docker安装配置Jupyter并配置R语言与Python环境

    文章目录 Docker docker的安装 将当前用户添加到docker用户组 拉取一个镜像 创建配置目录 启动docker服务 登录jupyter容器 安装vim 更换源 JupyterNotebook 设置python 安装Jupyte
  • 【burpsuite安全练兵场-服务端6】信息泄露漏洞-5个实验(全)

    前言 介绍 博主 网络安全领域狂热爱好者 承诺在CSDN永久无偿分享文章 殊荣 CSDN网络安全领域优质创作者 2022年双十一业务安全保卫战 某厂第一名 某厂特邀数字业务安全研究员 edusrc高白帽 vulfocus 攻防世界等平台排名
  • Vue中常用基础标签

    创建一个Vue实例 var vm new Vue el app data methods 在html中完整代码
  • 练习2-2 在不使用运算符&&或者

    1 for i 0 i lt lim 1 c getchar n c EOF i 2 s i c 3 4 5 while i lt lim 1 6 7 while c getchar EOF 8 9 while c getchar n 10
  • c语言基础总结之获取数组中元素最小值

    数组获取元素个数 sizeof ids sizeof int 需要根据字节的长度来计算个数 当然在java中直接 length来获取 c语言就是比较麻烦 毕竟java是封装语言 将繁杂的步骤分装好方便调用了 用随机数生成一个数组 写一个函数
  • Anaconda的安装与jupyter常用操作

    一 Anaconda的安装 关于Anaconda的在windows上的安装 我不做过多的赘述 大家可以参考博客 https ask hellobi com blog wangdawei 9786 这里 需要说明一下为什么选择Anaconda
  • 在虚拟机Docker安装MySQL8.0,Navicat连接数据库出错等踩过的坑

    Docker安装MySQL8 0 Navicat连接数据库 环境 虚拟机 CentOS 7 2 Docker 20 10 7 MySQL 8 0 27 安装MySQL 1 先创建两个MySQL使用文件夹 opt目录是Linux提供我们扩展的
  • 顺序表的定义和基本操作

    文章目录 顺序表的实现 静态分配 动态分配 顺序表的插入删除 顺序表的查找 总结 顺序表的实现 静态分配 include
  • 【C++入门】(拷贝)构造函数和析构函数

    1 构造函数和析构函数 1 构造函数 constructor 字面意思就是构造对象的函数 当我们定义类的对象时调用 可以帮我们完成对象的初始化 2 析构函数 destructor 可以看做是构造函数的逆过程 当销毁类的时候调用 做一些收尾的
  • 华为笔试面试机考

    学习目标 HJ2 计算某字符出现次数 学习内容 描述 写出一个程序 接受一个由字母 数字和空格组成的字符串 和一个字符 然后输出输入字符串中该字符的出现次数 不区分大小写字母 数据范围 1 n 1000 输入描述 第一行输入一个由字母和数字
  • DevOps教程:DevOps 架构

    注 本文译自 https www javatpoint com devops architecture 为了交付应用程序 开发和运营都扮演着至关重要的角色 部署包括需求分析 设计 开发以及软件组件或框架的测试 运营包括软件的管理流程 服务和
  • Python中的“ @”

    一 介绍 这是Python装饰器的语法 使用 符号 表示将装饰器函数放在被装饰函数的上方 当调用被装饰函数时 实际上是调用了装饰器函数 装饰器函数可以在调用被装饰函数之前或之后执行一些额外的操作 funA 作为装饰器函数 def funA
  • 10. TypeScript 交叉类型

    TypeScript 交叉类型 1 交叉类型 Intersection Types 是将多个类型合并为一个类型 interface Person1 handsome string interface Person2 high string
  • 数据结构-redis数据结构-跳表skiplist

    这篇文章简单分享学习redis 6 0 数据结构 跳表skiplist redis中的有序数据集合 zset 有两种实现方式 跳表和压缩列表 我们今天学习下跳表的实现原理 学习新的知识 我们先从已掌握的知识入手 由浅入深 让我们先从普通链表