当前位置: 首页 > news >正文

redis面试(四)ZSet数据结构

Sorted Set

有序集合ZSet,但是有序集合的英文明明是sorted sets。 那这个“Z”代表什么意思,这点官网没有解释,但是gitHub上有人问过,作者是这样回答的

Hello. Z is as in XYZ, so the idea is, sets with another dimension: the
order. It’s a far association… I know 😃

这句话的本意是:这里的Z就像XYZ中的Z一样,所以这个概念是,集合中还有另一个维度:顺序。这是一个遥远的关联…

在我理解来的话,这应该只是为了和原本的Sets集合做一个区分,Z代表的就是原本顺序之外的一个规则,就像XYZ中的Z一样,是二维平面之外,另一个维度的规则。

结构

在redis 7.0之前有两种编码:ziplist、skiplist
7.0之后是listpack、skiplist
主要区别就是ziplist和listpack

我们先来简单说一下两种格式:(如果了解跳表,可以直接跳转到 skiplist 看跳表的结构)

ziplist和listpack:都是一种压缩列表的实现,当保存的元素长度都小于64字节,同时数量小于128时,会使用该结构(可以认为就是有序列表 list )
与我们之前的理解list不同的地方是,他们占用的磁盘是连续的,没有节点之间的指针,而是将数据按照顺序一个个的排列。
那么查询的时候就要从第一个节点一个个的往后捋出来。虽然不会造成碎片空间,但这也是压缩列表的局限性。

ziplist

下面是这个ziplist列表的节点数据, 可以看到里面的属性 prevrawlensize,这个属性标记了前一个节点长度。

缺点就是 这个列表如果要更新第一个节点数据的话,可能会造成后面所有节点的长度数据全部更新。
(题外话:其实之前讲的redis的list结构中,每个节点Node里面都是一个ziplist,只不过我们只需要知道就可以了,在使用的时候不需要关心这些)

typedef struct zlentry {unsigned int prevrawlensize; /* 用于编码前一个节点字节长度*/unsigned int prevrawlen;     unsigned int lensize;        /* 用于编码此节点类型/长度的字节。例如,字符串有1、2或5个字节标题。整数总是使用一个字节。*/unsigned int len;            /* 用于表示节点实际的字节。对于字符串,这只是字符串长度而对于整数,它是1、2、3、4、8或0,具体取决于数字范围。 */unsigned int headersize;     /* prevrawlensize + lensize. */unsigned char encoding;      /* 设置为ZIP_STR_*或ZIP_INT_*,具体取决于节点编码。*/unsigned char *p;            /* 第一个节点的地址指针,prev-entry-len */
} zlentry;

listpack

listpack 列表最大的特点就是不再包含前一个节点的长度,那么在更新的时候就不会再造成连锁更新问题。
但是由于压缩列表本身的局限性,只能顺序查询,为了效率,在数据量超过64的时候,会变成跳表形式

typedef struct {/* 当使用string时,它具有长度(slen)。 */unsigned char *sval;uint32_t slen;/* 当使用integer时,“sval”为 NULL,lval 保存该值。*/long long lval;
} listpackEntry;

跳表zskiplist

跳表就是ZSets 有序列表的主要结构模式
skiplist 中也是包含两种结构,但是要注意,这里的两种结构是同时存在的 字典(dict)和跳跃表(zskiplist)存储方式。

dict就不说了,在上一章的Hash中说过,可以认为她就是一个k-v结构的数据。 里面的key是存储的数据,value是数据的score分数。
zskiplist:是一个具有跳跃节点能力的链表,给每个节点附加了一个level层级的属性,这个level会指向后面的 某一个节点,通过这个level层级可以直接越过中间的节点,减少查询的时间。
为了比较容易理解,这里画了一个示例图
在这里插入图片描述

L1、L2、L3… 这些就是每个节点的层级,规定了最高的层级是32层。 每个节点查询的时候,就可以通过高层直接跳跃到后面;
如果发现分数过大的话,可以通过低一些的层级少跳跃一些节点。

往里面放数据的时候,会给这个数据+分数封装为一个节点,然后给这个节点随机一个1~32范围内level的层高。
然后从头开始查询,通过level跳跃过N个节点,直接将节点放到对应的位置,然后给给每一层的level都添加一个指向下一个节点的指针。

具体实现结构如下:

typedef struct zskiplist {struct zskiplistNode *header, *tail;unsigned long length;int level;
} zskiplist;typedef struct zskiplistNode {struct zskiplistLevel {struct zskiplistNode *forward;unsigned int span;
} level[];
struct zskiplistNode *backward;
double score;
robj *obj;
} zskiplistNode;

Sorted Set 为什么使用跳跃表,而不是红黑树?

主要有以下几个原因:

  • 跳表的性能和红黑树差不多。
  • 插入速度非常快速,因为不需要进行旋转等操作来维持平衡性
  • 跳表更容易实现和调试。

跳表中的dict是什么用处?

通过上面的数据结构不难发下,跳表只适合单位查询,排序处理。但是不适合查询分数,以及判断成员是否存在这种操作。
那么这时候dict就派上用场了,之前说过,dict的结构是key-value键值对。
比如我们的数据是 周杰伦 100分、孙燕姿 99分、许嵩 98分
那在跳表zskiplist 中的数据是
(level:[], score:100分, value:周杰伦)
(level:[], score:99分, value:孙燕姿)
(level:[], score:98分, value:许嵩)

而在字典项中的数据是
(key:周杰伦, score:100分)
(key:孙燕姿,value:99分)
(key:许嵩, value:98分)

要查询某个数据是否存在,或者是查询分数的话,直接从dict的数据结构中通过key来取出分数就可以了,不需要在列表中查询。
这也是一种为了效率,把数据冗余一份的策略

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • JavaScript输出数据的方法?
  • uniApp跳转外链
  • 密码学基础-数据加密
  • 【学术会议征稿】第八届力学、数学与应用物理学国际会议(ICMMAP 2024)
  • mysql 各种锁归纳总结
  • FLOW MATCHING FOR GENERATIVE MODELING 阅读笔记
  • C++ primer plus 第17 章 输入、输出和文件:用cout进行格式化
  • Hibernate Validator 数据校验框架
  • 【从零开始一步步学习VSOA开发】创建VSOA的client端
  • poetry配置镜像
  • 【秋招笔试】2024-08-03-科大讯飞秋招笔试题(算法岗)-三语言题解(CPP/Python/Java)
  • DREAMLLM: SYNERGISTIC MULTIMODALCOMPREHENSION AND CREATION
  • C语言基础题:吃冰棍(C语言版)
  • Android笔试面试题AI答之Activity常见考点
  • AI智能测评应用平台项目分享
  • 【399天】跃迁之路——程序员高效学习方法论探索系列(实验阶段156-2018.03.11)...
  • 【css3】浏览器内核及其兼容性
  • 【技术性】Search知识
  • canvas 五子棋游戏
  • FastReport在线报表设计器工作原理
  • Linux快速配置 VIM 实现语法高亮 补全 缩进等功能
  • MySQL用户中的%到底包不包括localhost?
  • Promise面试题2实现异步串行执行
  • Unix命令
  • VUE es6技巧写法(持续更新中~~~)
  • 关于 Linux 进程的 UID、EUID、GID 和 EGID
  • 技术攻略】php设计模式(一):简介及创建型模式
  • 猫头鹰的深夜翻译:JDK9 NotNullOrElse方法
  • 前端存储 - localStorage
  • 前端知识点整理(待续)
  • 如何选择开源的机器学习框架?
  • 使用agvtool更改app version/build
  • 通过git安装npm私有模块
  • 一个6年java程序员的工作感悟,写给还在迷茫的你
  • 《天龙八部3D》Unity技术方案揭秘
  • Prometheus VS InfluxDB
  • 阿里云ACE认证学习知识点梳理
  • 格斗健身潮牌24KiCK获近千万Pre-A轮融资,用户留存高达9个月 ...
  • 我们雇佣了一只大猴子...
  • ​secrets --- 生成管理密码的安全随机数​
  • ​二进制运算符:(与运算)、|(或运算)、~(取反运算)、^(异或运算)、位移运算符​
  • # 数仓建模:如何构建主题宽表模型?
  • %3cli%3e连接html页面,html+canvas实现屏幕截取
  • (1)(1.9) MSP (version 4.2)
  • (2020)Java后端开发----(面试题和笔试题)
  • (23)Linux的软硬连接
  • (31)对象的克隆
  • (BAT向)Java岗常问高频面试汇总:MyBatis 微服务 Spring 分布式 MySQL等(1)
  • (附源码)springboot“微印象”在线打印预约系统 毕业设计 061642
  • (一)Kafka 安全之使用 SASL 进行身份验证 —— JAAS 配置、SASL 配置
  • (一)Thymeleaf用法——Thymeleaf简介
  • (转)MVC3 类型“System.Web.Mvc.ModelClientValidationRule”同时存在
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • .NET 4.0中的泛型协变和反变
  • .NET Core 通过 Ef Core 操作 Mysql