redis的基础底层篇 zset的详解

2023-09-16 07:07:38

一 zset的作用以及结构

1.1 zset作用

redis的zset是一个有序的集合,和普通集合set非常相似,是一个没有重复元素的字符串集合。常用作排行榜等功能,以用户 id 为 value,关注时间或者分数作为 score 进行排序。

1.2 zset的底层结构

1.zset是一个特别的数据结构,一方面它等价于 Java 的数据结构 Map<String, Double>,可以给每一个元素 value 赋予一个权重 score,另一方面它又类似于 TreeSet,内部的元素会按照权重 score 进行排序,可以得到每个元素的名次,还可以通过 score 的范围来获取元素的列表。

2.zset 底层使用了两个数据结构:

hash,hash 的作用就是关联元素 value 和权重 score,保障元素 value 的唯一性,可以通过元素 value 找到相应的 score 值。
跳跃表,跳跃表的目的在于给元素 value 排序,根据 score 的范围获取元素列表。
https://blog.51cto.com/u_14191/6345603

ZSet 有两种不同的实现,分别是 ziplist 和 skiplist。具体使用哪种结构进行存储,规则如下:

1.ziplist:满足以下两个条件:

[value,score]键值对数量少于128个;每个元素的长度小于64字节。

2.skiplist:不满足以上两个条件时使用skiplist跳表,组合了hash和skiplist

hash用来存储value到score的映射,在时间复杂度o(1)时间内知道对应value的分数。

skiplist按照从小到大的顺序存储分数;每个元素存储的都是<value,score>对。

1.3 zset的api接口

zrevrange key start stop [WITHSCORES]----------------返回按score从大到小排序后且索引在[start,stop]区间的元素,从0开始
zrevrangebyscore key max min[WITHSCORES]------------------返回按score从大到小排序后且分数在[min,max]区间的元素
zrangebylex key min max[LIMIT offset count]--------------通过字典区间返回有序集合的成员
zrank key member-------------返回元素member的索引,不存在nil
zrevrank key member--------------返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序
zscore key member---------------返回有序集合中,元素member分数
zscan key cursor [MATCH pattern] [COUNT count]-------------迭代有序集合中的元素(包括元素的分值)
zunionstore destination numkeys key [key …]-------------计算给定的一个或多个有序集的并集,并存储在新的 key 中
zinterstore destination numkeys key [key …]--------------计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中
zincrby key increment member-----------有序集合中对指定元素的分数加上增量 increment
zrem key member[member…]-----------移除有序集合中的一个或多个元素
zremrangebylex key min max-----------移除有序集合中给定的字典区间的所有成员
zremrangebyrank key start stop---------移除有序集合中给定的排名区间的所有成员
zremrangebyscore key min max-------移除有序集合中给定的分数区间的所有成员
https://blog.51cto.com/u_14191/6345603

二  跳跃表

2.1 跳跃表

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

数组支持随机访问的线性结构,底层使用二分查找。链表不支持随机访问的线性结构。跳跃表中查询任意数据的时间复杂度就是 O(logn)

2.2 跳跃表分析

1.对于一个单链表来讲,即便链表中存储的数据是有序的,如果我们要想在其中查找某个数据,也只能从头到尾遍历链表。这样查找效率就会很低,时间复杂度会很高,是 O(n)。

2.在该链表中,每隔一个节点就有一个附加的指向它在表中前两个位置上的节点的链,正因为如此,在最坏的情形下,最多考察 n/2 + 1 个节点。比如我们要查 90 这个节点,按照之前单链表的查找的话要 8 个节点,现在只需 5 个节点。 

3.这里每隔 4 个节点就有一个链接到该节点前方的下一个第 4 节点的链,只有 n/4 + 1 个节点被考察。

我们利用数学的思想,针对通用性做扩展。每隔第 2^i 个节点就有一个链接到这个节点前方下一个第  2^i  个节点链。链的总个数仅仅是加倍,但现在在一次查找中最多只考察 logn 个节点。不难看到一次查找的总时间消耗为 O(logn),这是因为查找由向前到一个新的节点或者在同一节点下降到低一级的链组成。在一次查找期间每一步总的时间消耗最多为 O(logn)。注意,在这种数据结构中的查找基本上是折半查找(Binary Search)。

2.3 跳跃表底层结构

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

上图最左边的是 zskiplist 结构,该结构包含以下属性:

  • header:指向跳跃表的表头节点

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

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

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

位于 zskiplist 结构右侧是四个 zskiplistNode 结构,该结构包含以下属性:

  • 层(level):节点中用 L1、L2、L3 等字样标记节点的各个层,L1 代表第一层,L2 代表第二层,以此类推。每个层都带有两个属性:前进指针和跨度。前进指针用于访问位于表尾方向的其它节点,而跨度则记录了前进指针所指向节点和当前节点的距离。

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

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

  • 成员对象(obj):各个节点中的 o1、o2 和 o3 是节点所保存的成员对象。

 2.4 平衡树,跳跃表,hash之间的比较

Redis中ZSet的底层数据结构跳跃表skiplist,你真的了解吗?

更多推荐

线性代数的本质(七)——特征值和特征向量

特征值和特征向量本章特征值和特征向量的概念只在方阵的范畴内探讨。相似矩阵Grant:线性变换对应的矩阵依赖于所选择的基。一般情况下,同一个线性变换在不同基下的矩阵不同。仍然以平面线性变换为例,Grant选用标准坐标系下的基向量i,j\mathbfi,\mathbfji,j,线性变换TTT对应的矩阵为AAA,而Jenni

Unity 场景淡入淡出效果

一.使用Dotween,建议使用我的方式FindObjectOfType<SceneFadeInAndOut>().FadeIn(()=>{Debug.Log("FadeinMenuStartScene");});usingDG.Tweening;usingSystem;usingSystem.Numerics;usi

干洗店洗鞋店线上下单小程序方便快捷

在线下单是干洗店小程序最直接满足顾客使用需求的一项功能,让顾客能够直观了解到不同规格不同方式清洁衣物干洗的价格并且能够直接在线下单,能够极大缩短消费环节,提升下单可能性,管理后台还能够同步记录顾客订单数据,对门店的日常管理也大有帮助。上门取送的功能也是一个十分适合添加在干洗店小程序里的模块,对于时间或者交通不便的用户来

蓝牙技术|多快好省的苹果Find My查找定位方案商:北京自在科技

在电子市场里,各种蓝牙定位器品牌争奇斗艳,例如国外的TileMate和Slim,三星的GalaxySmartTag和GalaxySmartTag+,Chipolo的ONESpot,还有苹果的AirTag等等。而国内也有着不少优秀的品牌,如Nutale的Air和Pro,VanMoof最S3及X3电动单车,Belkin的S

yamot:一款功能强大的基于Web的服务器安全监控工具

关于yamotyamot是一款功能强大的基于Web的服务器安全监控工具,专为只有少量服务器的小型环境构建。yamot只会占用非常少的资源,并且几乎可以在任何设备上运行。该工具适用于Linux或BSD,当前版本暂不支持Windows平台。比如说,广大研究人员可以使用yamot来监控在家运行的RaspberryPi服务器。

leetcode21合并两个有序链表

题目:将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。示例1:输入:l1=[1,2,4],l2=[1,3,4]输出:[1,1,2,3,4,4]示例2:输入:l1=[],l2=[]输出:[]示例3:输入:l1=[],l2=[0]输出:[0]提示:两个链表的节点数目范围是[0,5

web端程序访问过慢时如何判断问题

当Web端程序访问过慢时,可以按照以下步骤进行问题判断和排查:1.确认问题:确认Web端程序访问真的过慢,可以通过以下方法进行测试:在不同浏览器和设备上测试访问速度,以排除个别设备或浏览器的问题。在不同时间段进行测试,以排除网络高峰期的影响。确认网络连接是否稳定,可以尝试在多个网络环境下进行测试。2.检查服务器性能:如

3D医学影像PACS系统源代码

一、系统概述3D医学影像PACS系统,它集影像存储服务器、影像诊断工作站及RIS报告系统于一身,主要有图像处理模块、影像数据管理模块、RIS报告模块、光盘存档模块、DICOM通讯模块、胶片打印输出等模块组成,具有完善的影像数据库管理功能,强大的图像后处理功能,提高了临床诊断准确率。二、三维影像重建支持三维影像处理功能;

代码签名:保护你的软件的安全性和完整性

代码签名是一种数字签名技术,用于保护软件的完整性和身份。它通过使用一个密钥对软件代码进行签名,确保代码在下载和安装过程中没有被篡改。代码签名证书是一种数字证书,用于证明代码签名者的身份和代码的完整性。以下是代码签名证书如何保护您的软件的详细说明:1,确保软件的完整性:代码签名证书可以确保您的软件在下载和安装过程中没有被

【cmake开发(5)】cmake 设置常规变量、环境变量、内置变量;cmake 带参数编译和 -D 选项; c++源码通过-D 选项的宏定义进行条件编译

文章目录一、CMake变量的定义1.1定义常规变量1.2打印变量1.3环境变量1.4持久缓存1.5持久缓存原理1.6内置变量二、带参数编译2.1我们可以看到了-D选项,一般配合option命令2.2c++源码通过-D选项的宏定义进行条件编译参考在【cmake开发(3)】中,我们设置了makeinstall安装目录。这就

「网页开发|后端开发|Flask」08 python接口开发快速入门:技术选型&写一个HelloWorld接口

本文主要介绍为网站搭建后端时的技术选型考虑,以及通过写一个简单的HelloWorld接口快速了解前端和后端交互的流程。文章目录本系列前文传送门一、场景说明二、后端语言技术选型三、后端框架技术选型Django特点Flask特点FastAPI特点Tarnado特点四、用Flask先来个最简单的HelloWorld五、在前端

热文推荐