学习
实践
活动
专区
工具
TVP
写文章
专栏首页Redis源码学习系列Redis源码学习之跳表
原创

Redis源码学习之跳表

  • 跳跃链表

跳跃链表简称为跳表(SkipList),它维护了一个多层级的链表,且第i+1层链表中的节点是第i层链表中的节点的子集。跳表作为一种平衡数据结构,经常和平衡树进行比较,在大多数场景下,跳表都可以达到平衡树的效率(查询节点支持平均O(lgN),最坏O(N)的复杂度),但实现和维护起来却比平衡树简单很多。(跳跃列表由 William Pugh 发明。他在 Communications of the ACM发表了《Skip lists: a probabilistic alternative to balanced trees》,在其中详细描述了他的工作)

  • 跳表在Redis中的使用
  1. 有序集合对象(SortedSet)底层实现之一
  2. 集群节点内部数据结构
  • 跳表在Redis中的实现
  1. 数据结构 跳表节点,包含了节点中存储的数据obj和对应的分值score,以及每个节点的层数结构,每层都包含着前进指针与跨度;除此之外,每个节点还包含一个后退指针,假设跳表只有一层,那么整个跳表将退化为双端链表。

这里需要明确一下跨度的定义:从前驱节点到自身经历的节点数(包含前驱节点在内),如下图连接线上数字即为箭头指向节点的在对应层的跨度。

跳表,持有跳表的结构包括指向跳表表头和表尾的指针,以及整个跳表的长度(即第一层的节点数,但不包含头结点),还有整个跳表最高层的层数。这里需要指出的是,表头节点是一个包含了所有层的虚拟节点(不包含任何数据),每一层中表头节点的forward都指向该层的第一个真实节点。

最终,Redis中的一个长度为2,层高为2的跳表如下图所示

  1. 插入节点 当进行插入操作的时候,程序会维护两个数组,rank数组保存每层中插入节点前驱前驱节点的排行值,update数组保存每层插入节点的前驱节点,以下图为例:

在一个长度为4,高度为5的跳表中插入score为20,值为字符串c的节点,首先由上至下遍历每层查找插入位置,同时维护每层的rank值和update节点,遍历完之后,rank和update数组如图中所示,我们将其中的4层(即i=3)拿出来分析一下。在插入节点之前,update[3]指向头结点,rank[3]=0,rank[0]=2,第三层左边节点到右边节点的跨度是4,那么从图中可以看出,新节点的跨度=左边节点(即插入节点的前驱)跨度-(rank[0]-rank[3]),这里的rank[0]-rank[3]可以进一步模型化为rank[0]-rank[i],从上文中可以看出来,实际上rank[0]-rank[i]就表示,插入节点与插入节点前驱节点之间的节点数(不包含前驱节点自身),所以在原跨度中将其减掉,就是插入节点的跨度了。

当节点插入后,如下图所示。可以通过图中公式计算出插入节点的前驱节点的跨度。

整个例子走下来之后,我们可以通过流程图来回顾一下:

代码如下:

  1. 删除节点(内部函数) 删除节点被多个public方法调用,用于删除某个分值为score,对象为obj的节点。需要外部方法遍历节点各层后,维护update数组作为输入,需要注意这里跨度值的维护,代码实现如下:
  1. 删除节点(公开方法) 此方法对外公开,首先从上至下遍历各层维护好update数组,再调用内部删除节点方法,代码实现如下:
  1. 获取指定节点在跳表中的排行值 有了插入代码中排行值的讲解,相信你自己也可以实现这部分代码了,只需要在遍历的同时将跨度进行累加即可,代码实现如下:
  • 综述 上文中笔者只列出了自认为比较核心的方法,通过这几个方法也基本可以了解Redis中跳表的实现思想,总的来说与常规实现方法差别不大,只是对于排行值的需求定义并维护了每个节点的跨度。另外,值得一提的是,Redis中跳表的最高层数为32,层数越高出现的概率越低。在后面对于有序集合对象的介绍中,还会再次涉及到跳表。

原创声明,本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

登录 后参与评论
0 条评论

相关文章

  • Redis源码剖析之跳表(skiplist)

    计算机领域有很多种数据结构,数据结构的存在要么是为了节省时间、要么是为了节省空间,或者二者兼具,所以就有部分数据结构有时间换空间,空间换时间之说。其实还有某些以...

    xindoo
  • Redis源码之跳表数据结构

    跳跃表(skiplist)是一种随机化的数据, 由 William Pugh 在论文《Skip lists: a probabilistic alternati...

    杨永贞
  • Redis03-Redis的数据结构之跳表

    上一篇文章我们介绍了字典这个数据结构,这一篇文章我们接着来学习下另外一个数据结构,跳表。那么什么是跳表呢?

    码农飞哥
  • Redis源码学习之链表

    Redis实现的是双端无环链表,pre指针指向其前置节点,next指针指向其后置节点,表头节点的pre属性和表尾节点的next属性为nil,节点值...

    里奥搬砖
  • Redis源码学习之字典

    在字典结构体中,包含了一组字典函数(dictType),通过封装的方法处理对应的操作,通常在字典初始化的时候对其进行配置。

    里奥搬砖
  • Redis源码学习之压缩列表

    压缩列表是列表对象、哈希对象和有序集合对象的底层实现之一。以列表对象为例,当列表节点都是比较小的整数或者比较短的字符串的时候,Redis就会选择压缩列表来做底层...

    里奥搬砖
  • Redis源码学习之对象系统

    在前面的文章中,我介绍了Redis的底层数据结构,但Redis对外提供的命令并没有直接使用它们,而是基于它们构建更高级的数据对象,总共包括5中对象类型,分别为【...

    里奥搬砖
  • Redis源码学习之整数集合

    整数集合在Redis中是集合对象的底层存储之一,当一个集合对象的元素都是整数类型且元素数量不多(不超过512个)时,就会使用整数集合。

    里奥搬砖
  • 跳表(skiplist)的原理及concurrentskiplistmap的源码学习

    本文分为两个部分,第一个是对跳表(SKipList)这种数据结构的介绍,第二部分则是对Java中ConcurrentSkilListMap的源码解读.

    呼延十
  • Redis源码学习之字符串对象

    前文中提到,Redis的字符串对象的底层数据结构有三种,分别是整数编码、EMBSTR编码和SDS编码。在不同使用场景下进行相互切换,起到节约内存的作用。

    里奥搬砖
  • redis源码之SDS

    的时候,key和name都是字符串类型,而且字符串(string)在redis中是会经常用到的类型,那redis是如何保存字符串的呢?我们接下来往下看 众所周知...

    程序员小饭
  • redis源码之dict

    大家都知道redis默认是16个db,但是这些db底层的设计结构是什么样的呢?我们来简单的看一下源码,重要的字段都有所注释

    程序员小饭
  • 【redis源码学习】redisObject

    使用的是redis6.0.6版本,因为我第一次接触 redis 时它就是这个最新稳定版。

    看、未来
  • 【redis源码学习】redis 专属“链表”:ziplist

    用过 Python 的列表吗?就是那种可以存储任意类型数据的,支持随机读取的数据结构。 没有用过的话那就没办法了。

    看、未来
  • Redis源码剖析之RDB

    我们小学三年级的时候就知道,redis是一个纯内存存储的中间件,那它宕机会怎么样?数据会丢失吗?答案是可以不丢。 事实上redis为了保证宕机时数据不丢失,提供...

    xindoo
  • redis源码之set结构

    关于set的命令和常用场景我们暂时先不说了,如果对命令不太熟悉的朋友可以用 help @set命令查看,我们先来看set中的一种现象

    程序员小饭
  • 【redis源码学习】跳跃表

    在redis中跳表主要应用于有序集合的底层实现。 这个别人怎么讲都意义不大,自己动手去写一下才知道其中的妙处与不容易。没有看起来那么好写。

    看、未来
  • 【redis源码学习】事件机制

    1、redis使用 IO 复用 实现网络通信。 2、在Linux环境下选用epoll模式。

    看、未来
  • redis学习之redis应用(四)

    Redis Java客户端有很多的开源产品比如Redission、Jedis、lettuce

    周杰伦本人

作者介绍

里奥搬砖

腾讯高级工程师

腾讯 · 高级工程师 (已认证)

专栏

精选专题

活动推荐

关注

腾讯云开发者公众号
10元无门槛代金券
洞察腾讯核心技术
剖析业界实践案例
腾讯云开发者公众号二维码

扫码关注腾讯云开发者

领取腾讯云代金券