redis 系列7 数据结构之跳跃表
一.概述
跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。在大部分情况下,跳跃表的效率可以和平衡树(关系型数据库的索引就是平衡树结构)相媲美,并且因为跳跃表的实现比平衡树要来得更为简单,所以有不少程序使用跳跃表来代替平衡树。
Redis使用跳跃表作为"有序集合键"的底层实现之一,如果一个有序集合包含的元素数量比较多,又或者有序集合中元素的成员是比较长的字符串时,Redis就会使用跳跃表来作为有序集合键的底层实现。
下面用到的命令zadd和zrange。 使用zadd 命令将多个成员(number)及其score值加入到有序集key中。number是有序集成员,score可以是整数值或双精度浮点数。使用zrange命令将返回有序集合中给定区间的元素,start从0开始,stop 结束下标。
-- zadd命令语法格式
ZADD key score member [[score member] [score member] ...]
-- zrange命令语法格式
ZRANGE key start stop [WITHSCORES]
例1:下面使用zadd将fruit-price作为一个有序集合键,每个节点元素包括score和number。其中 score是价格,number是水果名称。再使用zrange 读出有序集合元素。
127.0.0.1:6379> zadd fruit-price 5.0 banana 6.5 cherry 8.0 apple
(integer) 3
127.0.0.1:6379> zadd fruit-price 4.0 pear
(integer) 1
127.0.0.1:6379> zrange fruit-price 0 3 withscores
1) "pear"
2) "4"
3) "banana"
4) "5"
5) "cherry"
6) "6.5"
7) "apple"
8) "8"
在上例中fruit-price有序集合的所有数据都保存在一个跳跃表里面,其中每个跳跃表节点都保存了一款水果的价格信息,所有水果按价钱从低到高在跳跃表里面排序。对比链表和字典等数据结构在Redis内部广泛应用不同,Redis只在两个地方用到了跳跃表,一个是实现有序集合键,另一个是在集群节点中用作内部数据结构。
1.1 跳跃表的实现
Redis跳跃表由 redis.h/zskiplistNode和redis.h/zskiplist 两个结构定义,其中zskiplistNode结构用于表示跳跃表节点, 而zskiplist结构则用于保存跳跃表节点的相关信息,比如节点数量,以及指向表头节点和表尾节点的指针等等。
上图中展示了一个跳跃表示例,位于是左边的是zskiplist结构,该结构包括以下属性:
(1) header: 指向跳跃表的表头节点。这里为第一个zskiplistNode。
(2) tail : 指向跳跃表的表尾节点。这里为第四个zskiplistNode。
(3) level:记录目前跳跃表内,zskiplistNode节点中最大的层数(表头节点的层数不计算在内)。最大节点的层数是第四个zskiplistNode节点,值为5 (每个跳跃表节点的层高都是1到32之间的随机数)。
(4) length: 记录跳跃表的长度。也就是节点数量(表头节点不计算在内),这里值是3。
上图中右方四个zskiplistNode节点,包含以下属性:
(1) 层level : 每个节点中用L1,L2,L3等字样标记节点的各个层,每个层都带有两个属性,包括前进指针和跨度。在上图里连线上带有数字的箭头就代表前进指针, 而那个数字就是跨度。当程序从表头向表尾进行遍历时,访问会沿着层的前进指针进行。
(2) 后退(backward)指针: 节点中用BW字样标记节点的后退指针,后退指针在程序从表尾向表头遍历时使用。
(3)分值(socre) : 各个节点中的1.0,2.0,3.0节点所保存的分值,在跳跃表中,节点按各自所保存的分值从小到大排列。
(4)成员对象(obj): 各个节点中的01,02,03是节点所保存的成员对象。
1.2 跳跃表节点
下面对zskiplistNode和zskiplist两个结构进行更详细的介绍,跳跃表节点实现由redis.h/zskiplistNode结构定义。
typedef struct zskiplistNode{ //层
struct zskiplistNode{
//前进指针
struct zskiplistNode *forward;
//跨度
unsigned int span;
}level[]; //后退指针
struct zskiplistNode *backward;
//分值
double score;
//成员对象
robj *obj; }zskiplistNode;
(1) 层:跳跃表节点的level数组可以包含多个元素,每个元素都包含一个指向其他节点的指针,程序可以通过这些层来加快访问其他节点的速度,一般来说,层的数量越多,访问其他节点的速度就越快。
(2) 前进指针:每个层都有一个指向表尾方向的前进指针(level[i].forward属性),用于从表头向表尾方向访问节点。
如上图所示: 遍历是程序首先访问跳跃表的第一个节点(表头),然后从第四层(L4)的前进指针移动到表中的第二个节点。在第二个节点时,程序沿着第二层(L2)的前进指针移动到表中的第三个节点。在第三个节点时,程序同样沿着第二层(L2)的前进指针移动到表中的第四个节点。当程序再次沿着第四个节点前进指针移动时,遇到null,程序知道这时已经到达了跳跃表的表尾,于是结束这次遍历。
(3) 跨度:层的跨度(level[i].span属性)用于记录两个节点之间的距离:两个节点之间的跨度越大,它们相距就越远。 指向null的所有前进指针跨度都为0,因为它们没有连向任何节点。对于遍历操作只使用前进指针就可以完成了,跨度实际上是用来计算排位的。
(4) 后退指针:节点的后退指针(backward属性) 用于从表尾向表头方向访问节点。与前进指针不同,前进指针一次可以跳过多个节点,而每个节点只有一个后退指针,所以每次只能后退至前一个节点。
(5) 分值和成员:节点的分值(score属性)是一个double类型的浮点数,跳跃表中的所有节点都按分值从小到大来排序。节点的成员对象(obj属性)是一个指针,它指向一个字符串对象,而字符串对象则保存着一个SDS值。在同一个跳跃表中,各个节点保存的成员对象必须是唯一的,但是多个节点保存的分值却可以是相同的,分值相同的节点将按照成员对象在字典中的大小来进行排序,成员对象较小的节点会排在前面(靠近表头的方向)。
例2: 分值相同的,按成员对象来排序。
127.0.0.1:> zadd test 1.0 a
(integer)
127.0.0.1:> zadd test 1.0 c
(integer)
127.0.0.1:> zadd test 1.0 b
(integer)
127.0.0.1:> zrange test withscores
) "a"
) ""
) "b"
) ""
) "c"
) ""
1.3 跳跃表
仅靠多个跳跃表节点就可以组成一个跳跃表,但通过使用一个zskplist结构来持有这些节点,程序可以更方便地对整个跳跃表进行处理。
typedef struct zskiplist
{
//表头节点和表尾节点
struct skiplistNode *header, *tail;
//表中节点数量
unsigned long length;
//表中层数最大的节点的层数
int level;
}zskiplist
redis 系列7 数据结构之跳跃表的更多相关文章
- Redis 的底层数据结构(跳跃表)
字典相对于数组,链表来说,是一种较高层次的数据结构,像我们的汉语字典一样,可以通过拼音或偏旁唯一确定一个汉字,在程序里我们管每一个映射关系叫做一个键值对,很多个键值对放在一起就构成了我们的字典结构. ...
- Redis系列(十二):数据结构SortedSet跳跃表中基本操作命令和源码解析
1.SkipList Redis的sortedSet数据结构是有序不重复的(索引为唯一的,数据(score)却可以重复), 跳表是redis的一个核心组件,也同时被广泛地运用到了各种缓存地实现当中,它 ...
- Redis数据结构之跳跃表
跳跃表是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的. 一.跳跃表结构定义1. 跳跃表节点结构定义: 2. 跳跃表结构定义: 示例: 二.跳跃表节点中各种 ...
- redis源码分析之数据结构:跳跃表
跳跃表是一种随机化的数据结构,在查找.插入和删除这些字典操作上,其效率可比拟于平衡二叉树(如红黑树),大多数操作只需要O(log n)平均时间,但它的代码以及原理更简单. 和链表.字典等数据结构被广泛 ...
- Redis 底层数据结构之跳跃表
文章参考 <Redis 设计与实现>黄建宏 Redis(2) 跳跃表 跳跃表 跳跃表 skiplist 是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节 ...
- Redis数据结构:跳跃表
1. 跳跃表是有序集合(zset)的底层实现之一: 2. 由zskiplist和zskiplistNode组成: 3. 每个跳跃表节点的层数都是1-32之间的随机数(每创建一个节点的时候,程序会随机生 ...
- Redis数据结构之跳跃表-skiplist
在Redis中,zset是一个复合结构: 使用hash来存储value和score的映射关系 使用跳跃表来提供按照score进行排序的功能,同时可以指定score范围来获取value列表 结构 zse ...
- Redis源码解析之跳跃表(一)
跳跃表(skiplist) 有序集合(sorted set)是Redis中较为重要的一种数据结构,从名字上来看,我们可以知道它相比一般的集合多了一个有序.Redis的有序集合会要求我们给定一个分值(s ...
- Redis源码解析之跳跃表(三)
我们再来学习如何从跳跃表中查询数据,跳跃表本质上是一个链表,但它允许我们像数组一样定位某个索引区间内的节点,并且与数组不同的是,跳跃表允许我们将头节点L0层的前驱节点(即跳跃表分值最小的节点)zsl- ...
随机推荐
- centos6.5中 linux 升级内核
需要使用安装docker ,但是docker 需要版本在3.1及以上.但是虚拟机的是2.6所以需要升级 记录以下升级的时候报错以及解决的办法 遇到的错误有: 1.出现curl: (35) SSL co ...
- look back to 2018
只写展望怎么行,还是缺一篇总结.2018年几乎没有怎么发朋友圈,需要一些文字记录一下这一年发生的事. 去年的现在,2018年的开端,结束了研一上学期充实的生活,下学期一项艰巨的任务就是完成大项目,一个 ...
- SDN网络中hypervisor带来的控制器时延(Hypervisor位置的优化)
一,问题背景 1.介绍监督器大部分由软件实现,可灵活放置,高效的SDN网络虚拟化需要复杂的技术来放置hypervisor在合适的位置,才能提供租户最佳的性能.称为k-Network Hyperviso ...
- FCC学习笔记(二)
Nest an Anchor Element within a Paragraph 作为参考,再次看一看a元素的图示: 例如: <p>Here's a <a href="h ...
- hadoop2-HBase的Java API操作
Hbase提供了丰富的Java API,以及线程池操作,下面我用线程池来展示一下使用Java API操作Hbase. 项目结构如下: 我使用的Hbase的版本是 hbase-0.98.9-hadoop ...
- deepin 开机进入 initramfs,无法开机
原因 这个问题多是由于不正常关机造成文件系统出问题导致的 解决 fsck -t ext4 /dev/sda6 这个命令用于修复磁盘,一直选择y即可. 如果sda6 不行就换位sda1 再试. 执行完后 ...
- xshell登陆服务器步骤
Xshell远程连接服务器 打开xshell后找到左上角第一个“文件”点击,弹出来一个下拉框,选择“新建”点击(或者直接按下快捷键“Alt+n”). 点击“新建”之后就会出现下面这样一 ...
- H5测试点总结-UI测试、功能测试、兼容性测试、体验相关(弱网、资源、手机操作等)、安全性测试、性能测试
一.概述 1.1 什么是H5 H5 即 HTML5,是最新的 Web 端开发语言版本,现如今,大多数手机 APP 页面会用 H5 实现,包括 PC Web 站点也会用它开发实现.所以 Web 的通用测 ...
- 如何把if-else代码重构成高质量代码
原文:https://blog.csdn.net/qq_35440678/article/details/77939999 本文提纲: 为什么我们写的代码都是if-else? 这样的代码有什么缺点? ...
- 蓝鲸单机安装mysql问题记录
1.注意默认启动用户为mysql 2.由于执行指令用的root,有些文件会生成为root用户 3.注意将datadir basedir socket dir 修改称mysql chown mys ...