虚线 实现_redis跳跃表实现

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

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

redis 只在两个地方使用到了跳跃表,一个是实现有序集合键,另一个是在集群节点中用作内部数据结构。

1   跳跃表的实现

redis 的跳跃表由  redis.h/zskiplistNode 和 redis.h/zskiplist 两个结构定义,其中 zskiplistNode 结构用于表示跳跃表节点,而 zskiplist 结构则用于保存跳跃表节点的相关信息,比如节点的数量,以及指向表头节点和表尾节点的指针等等。

0b5249f9da0e7845aa3235855fa6b5d6.png


上面最左边是一个  zskiplist 结构。该结构包含以下属性:

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

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

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

4、length:记录跳跃表的长度,也就是跳跃表目前包含节点的数量(表头节点的层数不计算在内)。

位于 zskiplist 右方的是  4 个 zskiplistNode 结构。该结构包含以下属性:

1、层(level):节点中用 L1、L2 等字样标记节点的各个层,L1 代表第一层,L2 代表第二层,以此类推。每个层都带有两个属性:前进指针和跨度。

前进指针用于访问位于表尾方向的其他节点,而跨度则记录了前进指针所指向节点和当前节点的距离。在上面图片中,连线上带有数字的箭头就代表前进指针,而那个数字则代表跨度。

2、后退(backward)指针:节点中用 BW 字样标记节点的后退指针,它指向位于当前节点的前一个节点。后退指针在程序从表尾向表头遍历时使用。

3、分值(score):各个节点中的 1.0、2.0 是节点所保存的分值。在跳跃表中,节点按各自保存的分值从小到大排列。

4、成员对象(obj):各个节点的 o1、o2 是节点保存的成员对象。

注意:

表头节点跟其他节点的构造是一样的,只不过表头节点的这些属性不会用到,因此省略了这部分。

1.1    跳跃表节点

跳跃表节点的实现由 redis.h/ 在 skiplistNode 结构定义:

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

1、层

跳跃表节点的 level 数组可以包含多个元素,每个元素都包含一个指向其他节点的指针,程序可以通过这些层来加快访问其他节点的速度。一般来说,层的数量越多,访问其他节点的速度就越快。

每次创建一个新的跳跃表节点的时候,程序都会根据幂次定律(越大的数出现的概率越小)随机生成一个介于 1 和 32 之间的值作为 level 数组的大小,这个大小就是层的“高度”。

2、前进指针

每个层都有一个指向表尾方向的前进(level[i].forward 属性),用于从表头向表尾方向访问节点。

下面展示了带有不同层高的节点:

3684651d9bf5e19fed5d777812663fb1.png

下面用虚线表示出了程序从表头向表尾方向,遍历跳跃表中所有节点的路径:

2a0a5f5f8326a9b92246ac1fe0c72e83.png

1、迭代程序首先访问跳跃表的第一个节点(表头),然后从第四层的前进指针移动到表中的第二个节点。 

2、在第二个节点时,程序沿着第二层的前进指针移动到表中的第三个节点。 

3、 在第三个节点时,程序同样沿着第二层的前进指针移动到表中的第四个节点。 

4、当程序再次沿着第四个节点的前进指针移动时,它碰到一个 NULL,程序知道这时已经到达了跳跃表的表尾,于是结束这次遍历。

3、跨度

层的跨度( level[i].span 属性)用于记录两个节点之间的距离:

1、两个节点之间的跨度越大,它们相距得就越远。

2、指向 NULL 的所有前进指针的跨度都为0,因为它们没有连向任何节点。

跨度实际上是用来计算排位(rank)的。在查找某个节点的过程中,将沿途访问过的所有层的跨度累计起来,得到的结果就是目标节点在跳跃表中的排位。

举个例子,下图用虚线标记了在跳跃表中查找分值为 2.0、成员对象为 o2 的节点时,沿途经历的层:在查找节点的过程中,程序经过了两个跨度为1的节点,因此可以计算出,目标节点在跳跃表中的排位为2。

15c0e675e0568f97721a5aba074d1ba9.png

4、后退指针

节点的后退指针(backward 属性)用于从表尾向表头方向访问节点。


跟可以一次跳过多个节点的前进指针不同,因为每个节点只有一个后退指针,所以每次只能后退至前一个节点。

下图用虚线展示了如何从表尾向表头遍历跳跃表中的所有节点:

55e996c1454dfad25a5634519d6b89e5.png

程序首先通过跳跃表的tail指针访问表尾节点,然后通过后退指针访问倒数第二个节点,之后再沿着后退指针访问倒数第三个节点,再之后遇到指向 NULL 的后退指针,于是访问结束。

5、分值和成员

节点的分值(score属性)是一个 double 类型的浮点数,跳跃表中的所有节点都按分值从小到大来排序。

节点的成员对象(obj属性)是一个指针,它指向一个字符串对象,而字符串对象则保存着一个 SDS 值。

在同一个跳跃表中,各个节点保存的成员对象必须是唯一的,但是多个节点保存的分值却可以是相同的:

分值相同的节点将按照成员对象在字典中的大小来进行排序,成员对象较小的节点会排在前面(靠近表头的方向),而成员对象较大的节点则会排在后面(靠近表尾的方向)。

在下图中所示的跳跃表中,三个跳跃表节点都保存了相同的分值10086.0。

1c248ddd6f2bd66fc681f40d803c4ab5.png

但保存成员对象o1的节点却排在保存成员对象o2和o3的节点的前面,而保存成员对象o2的节点又排在保存成员对象o3的节点之前,由此可见,o1、o2、o3三个成员对象在字典中的排序为o1<=o2<=o3。

1.2    跳跃表

靠多个跳跃表节点就可以组成一个跳跃表,如下所示:

bb67f37225097aef4f66a6cec9f3739c.png

zskiplist 结构的定义如下:

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

通过使用一个 zskiplist 结构来持有这些节点,程序可以更方便地对整个跳跃表进行处理。比如快速访问跳跃表的表头节点和表尾节点,或者快速地获取跳跃表节点的数量(也即是跳跃表的长度)等信息,如下图所示:

13b43599111ce5ee72f2eed9a453d141.png

这样获取表头、表尾节点,表长,以及表中最高层数的复杂度均为 O(1)。

2   跳跃表 API

6eed3d10ae933320bee4f59703d9d9d9.png

3    回顾

  • 跳跃表是有序集合的底层实现之一, 除此之外它在 Redis 中没有其他应用。

  • Redis 的跳跃表实现由 zskiplist 和 zskiplistNode 两个结构组成, 其中 zskiplist 用于保存跳跃表信息(比如表头节点、表尾节点、长度), 而 zskiplistNode 则用于表示跳跃表节点。

  • 每个跳跃表节点的层高都是 1 至 32 之间的随机数。

  • 在同一个跳跃表中, 多个节点可以包含相同的分值, 但每个节点的成员对象必须是唯一的。

  • 跳跃表中的节点按照分值大小进行排序, 当分值相同时, 节点按照成员对象的大小进行排序。

参考资料

[1]  redis设计与实现

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

当代家长现状。。 | 今日最佳

世界只有3.14 % 的人关注了青少年数学之旅&#xff08;图源都市音酱&#xff09;太真实了↓ ↓ ↓

Prometheus(一):Web服务环境监控

&#xfeff;写在前面现每个后端的同学的日常都在跟服务(接口)打交道&#xff0c;维护老的比较大单体应用、按业务拆得相对比较细的新服务、无论企业内部用的&#xff0c;面向用户的前端的服务。流量大的有流量小的&#xff0c;有重要的有不那么重要的。但是&#xff0c;不管怎…

acwing Linux 租云服务器环境配置

今天给大家讲解acwing Linux 租云服务器&环境配置&#xff0c;这里以阿里云为例子给大家讲解一下如何租用这个云服务器&#xff0c;现在有阿里云、华为云、腾讯云、京东云这么几个大的服务系统&#xff0c;我个人是喜欢华为云的嘻嘻&#xff0c;因为个人比较喜欢华为公司&a…

Shell练习题(持续更新)

1.输出1-10echo {1..10} seq -s 1 10 #默认分隔符\n for((i1;i<10;i));do echo -n "$i ";done;echo #最后echo为了换行 i1;while [ $i -le 10 ];do printf "%s " $i;i$[$i1];done;echo awk BEGIN{for(i1;i<10;i) printf "%s ",i;};…

楼层效果_1一28高楼最好最吉利的楼层是哪层?选楼层要注意什么?

在选择房屋楼层的时候&#xff0c;可能大家对于想过了&#xff0c;方法是比较纠结的&#xff0c;有些家庭可能考虑到室内的采光问题&#xff0c;有些可能考虑的是噪音的问题&#xff0c;更有些朋友们可能考虑的是室内的风水等相关问题&#xff0c;那么1一28高楼最好最吉利的楼层…

43秒处竟惊现刘强东!印度动作大片《WAR》终极预告曝光

全世界只有3.14 % 的人关注了青少年数学之旅都说印度是一个非常奇妙的国度&#xff0c;文能Z教治国&#xff0c;武能高产神片。科学家们骂骂咧咧地退出了群聊...这不&#xff0c;前不久印度又曝光了一部动作大片《WAR》&#xff1a;电影的剧情大概是讲述了一名印度士兵被派去消…

按id进行查找按名称进行排序_Excel工作表中如何按需要的顺序快速进行排序

在工作中&#xff0c;有时候需要经常对一组内容按一定的顺序来进行排序。如果每次都手动进行排序&#xff0c;会小号很多时间。因此可以将特定顺序添加到自定义序列中。有两种不同的操作方法可以来实现。方法一如图&#xff0c;现在有一列水果名称&#xff0c;之后需要都按照现…

20种最先进的机器人,感觉有点吓人!

全世界只有3.14 % 的人关注了青少年数学之旅随着科技的发展&#xff0c;机器人必然逐渐进入我们的生活&#xff0c;甚至在许多领域替代人类。以下是目前全球范围内最先进的一些机器人&#xff1a;Actroid-F这种实验性机器人的目标&#xff0c;是创造最逼真的仿人类机器人&#…

PDF批量删除注释

使用Adobe Acrobat Pro的批量处理完成 1.在“自定义”-“添加新工具集”-“动作向导”-将“创建新动作”加入-“保存”2.然后点新出现的“创建新动作”图标3.将“内容”-“删除所有注释”添加上&#xff0c;然后再添加上“保存和导出”中的保存4.保存设定的动作列表&#xff0c…

怎么让图片手机上排列_荣耀手机系列档次怎么排列?

目前&#xff0c;我们按照处理器和手机表现进行排列。荣耀magic2因为充电口(塑料问题)&#xff0c;所以&#xff0c;排名第一的位置我给荣耀V20&#xff01;其次&#xff0c;是荣耀magic2&#xff0c;然后是荣耀10&#xff0c;荣耀note10&#xff0c;其次是荣耀v10&#xff0c;…

金蝶显示服务器异常,金蝶提示云服务器异常

金蝶提示云服务器异常 内容精选换一换生命周期是指弹性云服务器从创建到删除(或释放)历经的各种状态。当云服务器网络异常、防火墙未放行本地远程桌面端口、云服务器CPU负载过高等场景均可能导致云服务器无法正常登录。当您的云服务器无法远程登录时&#xff0c;我们建议您首先…

Ajax 缓存

今天遇到ajax缓存的问题,指定同样的url和参数值总是有缓存&#xff0c;跳不到后台程序里&#xff01; 我先用了一种龌龊的方法,就是在url后又加了个由数据数产生的参数var randMath.random();var url"A.aspx?t"escape(t)"&r"escape(rand); 不知道大家…

S5PC100基于I2C子系统的lm75驱动流程图

转载于:https://blog.51cto.com/387929/1312237

如何有效利用碎片时间?这里有个办法

全世界只有3.14 % 的人关注了青少年数学之旅用了这么久微信&#xff0c;关注的公众号没有上千个&#xff0c;也有上百个&#xff0c;但其实大部分每天主动打开的公众号只有寥寥几个。今天就为大家推荐一些颜值与才华并存的好评公众号&#xff0c;希望你喜欢。【关注方式 】1 - …

gRPC四种模式、认证和授权实战演示

前言上一篇对gRPC进行简单介绍&#xff0c;并通过示例体验了一下开发过程。接下来说说实际开发常用功能&#xff0c;如&#xff1a;gRPC的四种模式、gRPC集成JWT做认证和授权等。正文1. gRPC四种模式服务以下案例演示&#xff0c;服务端用微软提供的模板创建&#xff0c;客户端…

ajax mode,DWR的三种Reverse Ajax Mode配置详解

Full Streaming ModeEarly Closing ModePolling Mode在DWR应用中.默认启用的是Full Streaming Mode .它是三种modes中最快的一种.它每60秒检查一次浏览器是否是活跃的.配置很简单:dwr-invokerorg.directwebremoting.servlet.DwrServletactiveReverseAjaxEnabledtrue然后在要使用…

摄像头预览左右翻转_轻薄翻转触控本里的高性能机型:惠普ENVY x360 13评测

自从今年3月末AMD锐龙4000系列处理器正式登陆移动平台以来&#xff0c;目前已有多款机型上市&#xff0c;并涌现出了不少高热度、高性价比的机型。我们也体验测试了其中数款机型&#xff0c;总体上给用户的印象是是价格香、性能强。而今天牛叔要聊的这款机型&#xff0c;也是搭…

聊一聊关于聊天记录的存储

背景 即时通讯(Instant Messaging)&#xff0c;也就是我们常说的 IM&#xff0c;其实在很多业务场景上都会有或多或少的应用&#xff0c;有的会是核心&#xff0c;有的会是辅助。既然是聊天&#xff0c;那么必然就会产生聊天记录&#xff0c;而且聊天记录随着人数的增加和时间的…

考勤机信息同步不到服务器,考勤机实时同步功能上线啦

一、考勤机管理重磅上线&#xff0c;告别手动处理打卡数据考勤机打卡仍然是当前企业主流的考勤方式&#xff0c;而考勤机打卡数据的核算也是HR最头痛的一点。HR每个月都要去电脑打开复杂难用的考勤机配套软件&#xff0c;导出并整理打卡数据&#xff0c;逐个统计员工的迟到、早…

ios gb2312转utf-8

2019独角兽企业重金招聘Python工程师标准>>> 靠&#xff0c;想写个爬虫功能&#xff0c;结果一直是乱码&#xff0c;终于找到了转码的方法了 (NSString *) gb2312toutf8:(NSData *) data{ NSStringEncoding enc CFStringConvertEncodingToNSStringEncoding(kCFStr…