redis跳表简介

摘要:
关于这个随机函数,我还没有做过彻底的研究。3.为什么Redis选择跳过表?严格来说,Redis也使用哈希表,但本文是关于跳过表的,因此暂时忽略它!在Redis开发手册中,有序收集支持的操作包括插入数据、删除数据、查找数据、迭代和输出有序序列。数据可以通过使用区间前面的其他数据结构找到-例如红黑树,但最后一个显然更好地使用跳转表来定位起点!

转自:https://baijiahao.baidu.com/s?id=1625500811386005937&wfr=spider&for=pc

一、前言

跳表(Skip List)这种数据结构在一般的数据结构书籍和算法书籍里都不怎么涉及----至少我大学数据结构课程没有讲这种结构。但是跳表确实是一种性能比较优秀的动态数据结构,并且Redis中的有序集合(Sorted Set)就是用跳表实现的。本文先大致了解一下跳表。

二、跳表

1、引出

对于一组有序数据,我们想要在其中查找某个数,如果数据使用数组存储,显然二分法是再适合不过了,但是如果数据是用链表存储的呢?难道我们用从头遍历吗?这样时间复杂度会达到O(n)级别,相比二分法O(logn)级别简直天壤地别。那么如何提高效率呢?

链表的查找时间复杂度算法为1/n(1+2+3+…+n)=O(n),数据出现的位置从第一个到最后一个的概率均为1/n,但是查询时间分别是1,2,,,n,所以平均时间复杂度为O(n)。

2、跳表

我用图片形式来理解跳表。

redis跳表简介第1张

如下图,对初始链表做一层“索引”,每两个节点提取一个节点到上一层,然后用down指针连接到下一层。

redis跳表简介第2张

现在我们查询16这个节点。从第一级索引开始,找到13,并且下一个为17,显然16在这两个节点之间,利用down指针下降一层,这次我们遍历2次即可。利用索引后,遍历了5+2=7次,而原始链表需要10次,这里加一层索引遍历次数减少了,效率提高了一点,但还不够,我们继续往上添加索引层。

redis跳表简介第3张

这里我不再算了,结果是6次,效率又提高了!

那么这种链表加多级索引就是跳表的结构了。

3、链表查询的时间复杂度是多少?

①假设链表有N个节点,按图所示依次往上级添加索引,第一级有N/2个节点,第二级有N/4个节点。。。。

那么第k级索引有N/(2^k)个节点。

②假设索引有h级,最高级有2个节点。就有N/(2^h)=2--->h=log2N-1(以2为底N的对数)。加上原始链表,那么高度就是log2N了。

③查询时,如果每层都遍历m次(最高级最多遍历2次,其他级最多遍历m次)那么复杂度就是O(mlgN)(在大O表示法里,logaN级别的复杂度等于lnN复杂度,去掉m就是O(logn)级别了),我们求一下m的值。

redis跳表简介第4张

在第k级时,我们遍历到了y和z,查询值介于两者之间,通过down指针,到达第k-1级,而这一级y和z之间最多有3个节点,那么m就等于3了。

综上跳表的查询时间复杂度就是O(logn)了。

4、跳表的耗费空间吗?

这个问题就很简单了,空间复杂度就是每层节点和,即n/2+n/4+...+4+2=n-2,空间复杂度就是O(n)了。

当然了这里也可以扩大索引间隔,减少一点索引空间,但是我们还是按照上面的求时间复杂度方法,得到的结果仍是O(logn)。实际应用中,向来是比较乐意用空间换时间的,所以这种做法的意义并不大,因为时间复杂度还是没变!

并且这里我们使用整数作为例子来讲得,软件开发中,链表存的可能是一个很大的对象,索引只需要记录关键字和一些指针,那么额外的空间和原数据相比完全可以忽略!

5、高效的动态插入和删除

我想汉字没有图片表达的清楚,所以还是用图片来表述!

redis跳表简介第5张

时间复杂度仍是O(logn)。我们说单链表插入复杂度为O(1),但是这是指插入动作,并不包含查找插入点所耗的时间,加上查找时间O(n),跳表效率还是高一点!

删除要麻烦一点,因为删除的节点要是在索引中,我们还得更新索引,更新索引就得找到前驱节点,当然双链表可以不用考虑了!

6、索引动态更新

考虑这样一种情况

redis跳表简介第6张

更极端的可以退化成单链表,所以索引的动态更新是必要的!

AVL树是通过左右旋转保持平衡性,而跳表是通过随机函数生成一个值K,然后将节点添加到第一级到第K级索引中。

redis跳表简介第7张

关于这个随机函数,我没有做深入研究(水平有限,然后有兴趣可以参考redis中关于有序集合的跳表实现)

三、Redis为什么选择跳表?

严格讲Redis还用到了散列表,但是本文讲的是跳表,所以暂时忽略!

在Redis开发手册中,有序集合支持的操作有

插入一个数据删除一个数据查找一个数据迭代输出有序序列按照区间查找数据前面可以用其他数据结构完成----比如红黑树,但是最后一个显然用跳表去定位起点(然后逐条输出)更好实现!再者跳表的代码虽然很难但是比起红黑树相对起来要好实现。

但是,红黑树有现成的实现,直接拿来用,而跳表却需要自己实现

免责声明:文章转载自《redis跳表简介》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇spark 作业调度AcWing 2879. 画中漂流(简单DP)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

MySQL查询语句分析 explain用法

explain显示了mysql如何使用索引来处理select语句以及连接表。可以帮助选择更好的索引和写出更优化的查询语句。 使用方法,在select语句前加上explain就可以了,如: explain select * from statuses_status where id=11; explain列的解释table:显示这一行的数据是关于哪张表的...

mysql explain中key_len的计算

ken_len表示索引使用的字节数,根据这个值,就可以判断索引使用情况,特别是在组合索引的时候,判断是否所有的索引字段都被查询用到。 key_len显示了条件检索子句需要的索引长度,但 ORDER BY、GROUP BY 子句用到的索引则不计入 key_len 统计值; 关于 key_len 的计算规则: • 当索引字段为定长数据类型,比如:char,in...

(转载)数据库表分割技术浅析(水平分割/垂直分割/库表散列)

一、数据库表分割技术   数据库表分割技术包含以下内容: 水平分割 垂直分割 库表散列 1.1、水平分割   什么是水平分割?打个比较形象的比喻,在食堂吃饭的时候,只有一个窗口,排队打饭的队伍太长了,都排成S型了,这时容易让排队的人产生焦虑情绪,容易产生混 乱,这时一个管理者站出来,增加多个打饭窗口,把那条长长的队伍拦腰截断成几队。更形象一点的理解,你...

redis 在 php 中的应用(key篇)

本文为我阅读了redis参考手册之后结合博友的博客编写,注意 php_redis 和 redis-cli 的区别(主要是返回值类型和参数用法) 目录: KEY(键) DEL EXISTS EXPIRE EXPIREAT keys MOVE PERSIST TTL RANDOMKEY RENAME RENAMENX TYPE SORT KEY(...

Docker中运行redis报错误: Failed opening the RDB file root (in server root dir /etc/cron.d) for saving: Permission denied

错误信息: 1:M 23 Dec 2021 19:53:02.058 * Background saving started by pid 1848 1848:C 23 Dec 2021 19:53:02.058 # Failed opening the RDB file backup.db (in server root dir /etc) for sa...

redis-cluster集群安装(基于redis-3.2.10)

上节主要演示了redis单节点的安装部署,对于数据量更大的服务可以安装redis-cluster进行处理 1. 安装ruby yum install ruby ruby-devel rubygems rpm-build -y rpm -qa|grepruby ruby -v 2.安装redis-cluster gem install redis --v...