Redis中的有序集合

前言

本文着重介绍Redis中的有序集合的底层实现中的跳表

有序集合 Sorted Set

Redis中的Sorted Set 是一个有序的无重复值的集合,他底层是使用压缩列表和跳表实现的,和Java中的HashMap底层数据结构(1.8)链表+红黑树异曲同工之妙

什么是跳表

跳跃表(Skip List)是一种有序的数据结构,它由多层有序链表组成。每一层链表中的节点是有序排列的,而每一层链表中的节点指针可以跨越若干个节点,这样就提高了查询效率。

  1. 跳跃表最底层的链表包含了所有的元素,而每一层链表都是下一层链表的子集。最顶层链表只有两个节点,即头节点和尾节点。每一层链表中的节点包含了一个成员(Member)和一个指向同样成员的下一层链表节点的指针。

  2. 通过使用跳跃表,可以在时间复杂度O(logN)的情况下执行插入、删除和查找操作。这相比于传统链表的时间复杂度O(N)来说更加高效。同时,跳跃表还相对于平衡二叉树来说更加简单,容易实现。

其实就是经典的空间换时间,利用“跳跃节点”在层级间跳跃,每层都保留数据,从下往上,数据越来越少,图示如下:
在这里插入图片描述

跳表的查询流程

跳表的查询流程其实很简单,举个例子假如我们要查找value == 6,正常链表需要遍历4次才能找到,而跳表3次就可以了。
其过程就是,1->1->2->6,1->1这个直接过去的,不需要额外判断,也就是1->2->6这个过程,图示如下:
在这里插入图片描述
其原理就和二分查找一样,首先顶层的1节点判断小于就往下右走到第二层的2节点,然后往右走,就找到6了

同样的,如果查找4节点,1->1->2->2->4,在一层中如果下一个节点的值比目标值还大的值就直接往下走,因为下层的数据的范围一定在[Node.value,Node.next.value]之间,当前例子中就是4一定在下一层的[2,6]节点中。这样就可以做到快速访问了,在数据量大的情况下,他的时间复杂度就是O(logN)。

跳表的插入

在Redis中,跳表的每一层链表都有一个编号,从下往上是0~31,当我们要进行插入操作的时候,Redis 会生成一个随机数,这个随机数的范围是[1,32],这个随机数越大,生成的概率就越小,意思就是生成1的概率为50%,2的概率为25%,逐层减半。源码如下:

static unsigned int ziplistLength(unsigned char *zl) {
    return ziplistLen((unsigned char*)zl);
}

// 生成一个介于1和2^32之间的随机数
static unsigned int zrandom(void) {
    // 以秒为种子生成随机数
    srand(time(0));
    return rand();
}

typedef struct zskiplistNode {
    // 成员对象
    sds ele;
    // 分值
    double score;
    // 后退指针
    struct zskiplistNode *backward;
    // 层
    struct zskiplistLevel {
        // 前进指针
        struct zskiplistNode *forward;
        // 跨度(跨过的节点数量)
        unsigned int span;
    } level[];
} zskiplistNode;

typedef struct zskiplist {
    // 头节点和尾节点
    struct zskiplistNode *header, *tail;
    // 节点数量
    unsigned long length;
    // 层数
    int level;
} zskiplist;

// 创建并返回一个新节点
static zskiplistNode *zslCreateNode(int level, double score, sds ele) {
    // 分配节点空间
    zskiplistNode *zn = zmalloc(sizeof(*zn) + level * sizeof(struct zskiplistLevel));
    // 设置节点成员
    zn->score = score;
    zn->ele = ele;
    return zn;
}

// 创建并返回一个新的跳跃表
zskiplist *zslCreate(void) {
    int j;
    // 分配空间
    zskiplist *zsl = zmalloc(sizeof(*zsl));
    // 初始化头节点
    zsl->header = zslCreateNode(ZSKIPLIST_MAXLEVEL, 0, NULL);
    // 设置尾节点
    zsl->tail = NULL;
    // 初始化长度和层数
    zsl->length = 0;
    zsl->level = 1;
    // 初始化头节点的 forward 和 span
    for (j = 0; j < ZSKIPLIST_MAXLEVEL; j++) {
        zsl->header->level[j].forward = NULL;
        zsl->header->level[j].span = 0;
    }
    // 初始化尾节点
    zsl->header->backward = NULL;
    return zsl;
}

// 随机生成节点层数
int zslRandomLevel(void) {
    int level = 1;
    // 每隔2个节点,层数+1(以1/4的概率)
    while ((zrandom() & 0xFFFF) < (ZSKIPLIST_P * 0xFFFF)) {
        level += 1;
    }
    return (level < ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}

这个随机数的意义就是数据在插多少层,举个例子,假如我随机到5,那么就意味着我的数据要从的0~4层进插入,这样才能保证我新来的数据不会影响到我跳表下层兼容上层的特性,也能保证数据访问的快速性(因为不一定要到底层查数据有可能我上一层就查到了),图示如下(random== 2,value== 5):
在这里插入图片描述

压缩列表升级为跳表

在Java的HashMap中,是要到table.length >= 64 && list.length >= 8 时才会出现一个从链表升级到红黑树的过程,在我们的Redis中也是如此,只要不满足其中一个,就会升级 (||运算)

1. 有序集合中的元素个数小于128个
2. 有序集合保存的元素成员长度都必须小于64字节

当以上任意一个不满足时,就会从压缩列表升级为跳表

以上是本节的全部内容

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/83486.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

记一次mysql8 在linux上安装全过程

参照MYSQL官网官方文档安装 1、mysql官网 mysql官网 2、直接进入文档页 找到安装文档 3、找到自己系统对应的安装文档&#xff0c;选合适的安装方式&#xff0c;我这里使用的是YUM方式 a、开始安装之前需要替换yum仓库 具体步骤如下 b、将下载的文件上传至自己的服务器 如下…

什么是原型(prototype)和原型链(prototype chain)?如何继承一个对象的属性和方法?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 原型&#xff08;Prototype&#xff09;和原型链&#xff08;Prototype Chain&#xff09;⭐ 原型&#xff08;Prototype&#xff09;⭐ 原型链&#xff08;Prototype Chain&#xff09;⭐ 继承属性和方法⭐ 写在最后 ⭐ 专栏简介 前端入…

shell脚本之循环语句

循环语句 循环含义 将某代码段重复运行多次&#xff0c;通常有进入循环的条件和退出循环的条件 for循环语句 一般知道循环次数使用for循环 第一类 格式1&#xff1a; for名称 in 取值次数;do;done; 格式2&#xff1a; for 名称 in {取值列表} do done# 打印20次 for i i…

从 Ansible Galaxy 使用角色

从 Ansible Galaxy 使用角色 根据下列要求&#xff0c;创建一个名为 /home/curtis/ansible/roles.yml 的 playbook &#xff1a; playbook 中包含一个 play&#xff0c; 该 play 在 balancers 主机组中的主机上运行并将使用 balancer 角色。 此角色配置一项服务&#xff0c;以…

十、flume的安装

1.解压 2.改名 3.修改权限 4.编辑环境变量并source export FLUME_HOME/usr/local/flume export PATH$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$HIVE_HOME/bin:$HBASE_HOME/bin:$SQOOP_HOME/bin:$PIG_HOME/bin:$FLUME_HOME/bin 5.配置 6.查看版本 7.启动Hadoo…

LLM提示词工程和提示词工程师Prompting and prompt engineering

你输入模型的文本被称为提示&#xff0c;生成文本的行为被称为推断&#xff0c;输出文本被称为完成。用于提示的文本或可用的内存的全部量被称为上下文窗口。尽管这里的示例显示模型表现良好&#xff0c;但你经常会遇到模型在第一次尝试时无法产生你想要的结果的情况。你可能需…

31.Netty源码之客户端启动流程

highlight: arduino-light 客户端启动主要流程 如果看了服务器端的启动流程&#xff0c;这里简单看下就可以了。 java package io.netty.server; ​ import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import …

SHELL 基础 显示字符颜色, 修改历史命令,Linux里的命令 执行顺序

echo 打印命令 &#xff1a; 显示字符串 &#xff1a; [rootserver ~]# echo this is SHELL language this is SHELL language [rootserver ~]# echo this is SHELL language this is SHELL language [rootserver ~]# echo "this is SHELL language" this is SH…

VMware上搭建的虚拟机突然本地无法连接服务器

长时间没有使用VMware 虚拟机了&#xff0c;今天突然登录上去&#xff0c;启动虚拟服务器后发现本地等不了了&#xff0c; 经过排查发现是开启了&#xff1a;VirtualBox Host-Only Network 关闭之后就本机就可以直连服务器了

pytorch基础实践-数据与预处理

文章目录 数据集Fashion-MNIST 数据集 数据预处理包的导入在Pytorch中进行 ETL利用torchvison包获取和处理数据集&#xff08;ET&#xff09; 访问数据集访问和查看 train_set 中的单个数据利用 DataLoader 成批访问数据 数据集 Fashion-MNIST 数据集 MNIST MNIST&#xff0c;…

政务中心站至政务中心东站右线盾构本月始发

本报记者 赵鹏 实习记者 池阳 通讯员 董浩程 立秋已过&#xff0c;平谷线“瓜熟蒂落”的日子指日可待。在左线隧道刚刚顺利贯通后&#xff0c;平谷线政务中心站至政务中心东站区间右线隧道已展开盾构组装施工&#xff0c;右线盾构即将于本月内始发&#xff0c;被誉为“地下蛟龙…

如何查看Linux内核版本

如何查看Linux内核版本 uname -r用centos7.0&#xff0c;内核版本就是3.10

Cesium加载ArcGIS Server4490且orgin -400 400的切片服务

Cesium在使用加载Cesium.ArcGisMapServerImageryProvider加载切片服务时&#xff0c;默认只支持wgs84的4326坐标系&#xff0c;不支持CGCS2000的4490坐标系。 如果是ArcGIS发布的4490坐标系的切片服务&#xff0c;如果原点在orgin X: -180.0Y: 90.0的情况下&#xff0c;我们可…

设计模式之组合模式(Composite)的C++实现

1、组合模式的提出 在软件开发过程中&#xff0c;使用者Client过多依赖所操作对象内部的实现结构&#xff0c;如果对象内部的实现结构频繁发生变化&#xff0c;则使用者的代码结构将要频繁地修改&#xff0c;不利于代码地维护和扩展性&#xff1b;组合模式可以解决此类问题。组…

云养猪平台如何开发

随着数字化和智能化的发展&#xff0c;农业行业也逐渐开始融入互联网技术&#xff0c;其中云养猪平台作为新兴的农业数字化解决方案之一&#xff0c;备受关注。本文将探讨如何开发一款具备专业、思考深度和逻辑性的云养猪平台。 一、前期准备阶段&#xff1a; 1.明确目…

SecureCRT 备份Button Bar中所有Button

一、前言 Button Bar功能可以保存一些常用命令避免重复输入&#xff0c;但是有时候secureCRT的button bar经常莫名其妙消失&#xff0c;重装系统或软件后&#xff0c;也都需要重新一个个添加Button&#xff0c;如果能备份就能减少这些费时间的操作 二、备份步骤 在面板Optio…

数字孪生助力智慧水务:科技创新赋能水资源保护

智慧水务中&#xff0c;数字孪生有着深远的作用&#xff0c;正引领着水资源管理和环境保护的创新变革。随着城市化和工业化的不断推进&#xff0c;水资源的可持续利用和管理愈发显得重要&#xff0c;而数字孪生技术为解决这一挑战提供了独特的解决方案。 数字孪生技术&#xf…

typescript 声明文件

作用 1、为已存在js库提供类型信息&#xff0c;这样在ts项目中使用这些库时候&#xff0c;就像用ts一样&#xff0c;会有代码提示、类型保护等机制 2、项目内共享类型&#xff1a;如果多个.ts文件中都用到同一个类型&#xff0c;此时可以创建.d.ts文件提供该类型&#xff0c;…

常见的软件测试用例设计方法有哪些?

常见的软件测试用例设计方法&#xff0c;个人认为主要是下面这6种&#xff1a; 1)流程图法&#xff08;也叫场景法&#xff09; 2)等价类划分法 3)边界值分析 4)判定表 5)正交法 6)错误推测法 这6种常见方法中&#xff0c;我分别按照定义、应用场景、使用步骤、案例讲解这4个部…

# 59. python的类与对象-更新

[目录] 文章目录 59. python的类与对象-更新1.面向对象编程2.什么是类3.什么是对象4.如何描述对象5.对象的属性和方法6.Python中的类7.type()函数查看数据类型8.类在Python中的应用9.总结 【正文】 59. python的类与对象-更新 1.面向对象编程 本节内容特别抽象&#xff0c;初…