Redis数据结构—跳跃表 skiplist

一、引言

在数据库和缓存系统的世界中,Redis以其高性能、高可用性、丰富的数据结构以及简洁的API而备受青睐。Redis支持多种数据结构,包括字符串、列表、集合、有序集合等,每种数据结构都对应着一种或多种内部实现。其中,跳跃表(SkipList)作为一种重要的数据结构,被Redis用于有序集合(Sorted Set)的底层实现,以实现高效的插入、删除和查找操作。本文将深入探讨Redis中的跳跃表,包括其基本原理、实现细节以及应用场景,并附有详细的代码示例

二、跳跃表的基本原理

跳跃表是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。这种数据结构可以看作是对单链表的一种优化,通过添加多级索引来提高查找效率。具体来说,跳跃表中的每个节点都包含多个指针,这些指针按照从低到高的层次进行排列,每一层都构成了一个有序链表。查找时,从最高层开始,根据目标值的大小逐层向下查找,直到找到目标节点或确定目标节点不存在。

跳跃表的主要优势在于其查找效率。对于包含n个节点的跳跃表,其查找操作的时间复杂度为O(log n),这是因为每次查找都可以跳过部分节点,从而减少比较次数。与单链表相比,跳跃表的查找效率得到了显著提升。

三、Redis中的跳跃表实现

在Redis中,跳跃表被用作有序集合(Sorted Set)的底层实现之一。有序集合是一种元素不重复且按照成员分数(score)进行排序的集合。Redis使用跳跃表来维护有序集合中的元素顺序,以便实现高效的插入、删除和查找操作。

Redis中的跳跃表实现主要包括以下几个部分:

  1. 跳跃表节点(zskiplistNode):跳跃表的节点结构包含了多个指针,用于指向同一层次和下一层次的节点。每个节点还包含一个元素值(member)和一个分数(score),用于排序和比较。
  2. 跳跃表(zskiplist):跳跃表是一个包含多个节点和层级信息的结构体。它包含了表头节点(header)、表尾节点(tail)以及最大层级(level)等信息。表头节点和表尾节点用于快速定位跳跃表的边界,而最大层级则决定了跳跃表的高度。
  3. 跳跃表迭代器(zskiplistIter):跳跃表迭代器用于遍历跳跃表中的节点。它包含了当前节点指针、当前层级以及遍历方向等信息。

下面是一个简化的Redis跳跃表节点的C语言结构体定义:

typedef struct zskiplistNode {sds ele;           // 节点元素double score;      // 节点分数struct zskiplistNode *backward; // 回退指针// 层级数组,level 数组中的每个元素都包含两个指针:forward 和 spanstruct zskiplistLevel {struct zskiplistNode *forward;unsigned int span;} level[];
} zskiplistNode;

需要注意的是,上述结构体中的层级数组(level)是一个柔性数组(Flexible Array Member),它在C99标准中被引入,允许在结构体中定义一个未知大小的数组。在Redis中,每个节点的层级数量是不固定的,因此使用柔性数组可以方便地表示这种动态结构。

四、跳跃表的应用场景

Redis中的跳跃表主要被用于有序集合(Sorted Set)的底层实现。有序集合是一种元素不重复且按照成员分数进行排序的集合。通过跳跃表,Redis可以高效地实现有序集合的插入、删除和查找操作。具体来说,跳跃表在以下场景中发挥着重要作用:

  1. 排行榜:跳跃表可以很好地支持排行榜功能。例如,在游戏应用中,可以根据玩家的积分排名进行快速更新和查询。由于跳跃表的查找效率为O(log n),因此可以快速地定位到指定排名的玩家。
  2. 范围查询:跳跃表还可以用于支持范围查询操作。例如,在社交网络中,可以根据用户的年龄范围或地理位置范围来查找符合条件的用户。通过跳跃表的多级索引结构,可以快速定位到指定范围内的节点,从而高效地进行范围查询。
  3. 实时统计:跳跃表还可以用于实时统计数据的功能。例如,可以统计某个时间段内的用户活跃数、订单数量等。由于跳跃表的插入和删除操作时间复杂度较低,因此可以实时地更新统计数据并快速地进行查询。

五、代码示例

下面是一个简化的Redis跳跃表插入操作的C语言代码示例。这个示例主要展示了如何创建新的节点、计算新节点的层级、更新跳跃表以及处理插入过程中的一些细节。

首先,我们需要定义一些辅助函数和常量:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define ZSKIPLIST_MAXLEVEL 16  /* Skiplist的最大层级 */
#define ZSKIPLIST_P 0.25      /* 层级计算的概率 */typedef struct zskiplistNode {double score;char *ele;struct zskiplistLevel {struct zskiplistNode *forward;unsigned int span;} level[];
} zskiplistNode;typedef struct zskiplist {struct zskiplistNode *header, *tail;unsigned long length;int level;
} zskiplist;// ... 其他相关定义和函数// 创建一个新的跳跃表节点
zskiplistNode* zslCreateNode(double score, char *ele, int level) {zskiplistNode *zn = (zskiplistNode*)zmalloc(sizeof(zskiplistNode) + level*sizeof(struct zskiplistLevel));zn->score = score;zn->ele = strdup(ele);for (int i = 0; i < level; i++) {zn->level[i].forward = NULL;zn->level[i].span = 0;}return zn;
}// 随机计算一个节点的层级
int zslRandomLevel() {int level = 1;while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF) && level < ZSKIPLIST_MAXLEVEL) {level++;}return level;
}// 插入元素到跳跃表中
zskiplistNode* zslInsert(zskiplist *zsl, double score, char *ele) {zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;int rank[ZSKIPLIST_MAXLEVEL];int i, level;// ... 此处省略了查找和更新update数组的代码// 创建新节点并设置层级level = zslRandomLevel();if (level > zsl->level) {for (i = zsl->level; i < level; i++) {rank[i] = 0;update[i] = zsl->header;update[i]->level[i].span = zsl->length;}zsl->level = level;}x = zslCreateNode(score, ele, level);// 插入新节点到跳跃表for (i = 0; i < level; i++) {x->level[i].forward = update[i]->level[i].forward;update[i]->level[i].forward = x;// 更新spanupdate[i]->level[i].span++;}// ... 此处省略了更新表尾节点和长度的代码return x;
}// ... 其他相关函数(如查找、删除等)int main() {// 示例代码,创建跳跃表并插入元素zskiplist *myZsl = zslCreate(); // 假设zslCreate用于创建空的跳跃表zslInsert(myZsl, 1.0, "ele1");zslInsert(myZsl, 2.0, "ele2");// ... 插入其他元素和进行相关操作// 清理资源// ...return 0;
}

请注意,上述代码是一个简化的示例,并未包含所有Redis跳跃表实现的详细代码,特别是关于查找和删除操作的代码、表尾节点的更新、跳跃表长度的维护等部分被省略了。在实际应用中,还需要考虑内存管理、错误处理、并发控制等方面的问题。

六、总结

Redis中的跳跃表是一种高效的有序数据结构,它通过在节点中维护多级索引来加速查找操作。在Redis中,跳跃表被用作有序集合(Sorted Set)的底层实现之一,以支持高效的插入、删除和查找操作。通过深入理解跳跃表的基本原理和实现细节,我们可以更好地利用Redis提供的数据结构和功能,为应用程序提供高性能的数据存储和

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TDengine 签约精诚瑞宝,开拓更智能的 IT 服务和管理平台

在当今的数字化时代&#xff0c;数据不仅是企业运营的核心资产&#xff0c;更是推动业务创新和服务优化的关键驱动力。随着数据量的激增&#xff0c;企业面临的挑战也随之增加&#xff0c;尤其是在数据处理和分析的效率上。作为台湾信息服务产业领导厂商精诚集团的核心成员&…

昇思25天学习打卡营第2天|linchenfengxue

传统的计算机视觉方法通常包括图像预处理、特征提取、特征筛选、图像识别等几个步骤。 对于给定的数字图像&#xff0c;计算机在处理时要先执行二次采样、平滑去噪、对比度提升和尺度调整等预处理操作&#xff0c;再对图像中的线条、边缘等全局特征和边角、斑点等局部特征&…

嵌入式信号处理面试题及参考答案(持续更新)

什么是离散时间信号?与连续时间信号的主要区别是什么? 离散时间信号是一种仅在离散时间点上有定义的信号,它不连续存在于所有时间点上,而是只在特定的、通常是均匀间隔的时间点取值。这种信号的表示通常通过序列来完成,比如在数字信号处理中广泛应用的各种音频、视频或控…

MySQL 性能优化全面指南

MySQL 性能优化全面指南 优化MySQL性能是提升数据库响应速度、降低延迟和提高系统整体性能的关键。以下是一些常用的MySQL性能优化方法&#xff0c;涵盖了硬件、配置、查询、索引、架构等多个方面。 1. 硬件优化 1.1. 增加内存 确保有足够的内存来缓存索引和数据&#xff0…

全网最全图书管理后台系统 ---前端项目简介文档

仅供参考&#xff0c;请勿抄袭 图书管理后台系统 ---前端项目简介 Vue2基础脚手架Request http请求封装(src/utils/request.js)Axios--前端和后端的通信工具前端向后端发送请求&#xff0c;可以把数据传给后端&#xff0c;从后端提取数据 前端跨域解决方案 vue使用request.…

游戏心理学Day24

游戏与文化 1989年,哈佛大学政治学教授约瑟夫奈(Joseph Nye)创造出了“软实力”这个概念&#xff0c;指的是事物产生吸引力和说服力的一种影响力&#xff0c;而不是一种威胁力或强制力。2004年4月,约瑟夫的著作《软实力&#xff1a;世界政治中的制胜之道》引起世界热议。此后&…

录音怎么转文字更高效?5款软件带你轻松拿捏文本转换~

临近大学生们最难熬的期末考试周&#xff0c;如何在短时间内复习完所有必考的科目也就成为大家迫在眉睫的首要任务。 想要在复习的过程中&#xff0c;更加高效地捕捉和整理关键信息、提高学习效率&#xff0c;那么录音转文字免费应用无疑是你的一大好帮手&#xff01; 倘若你…

【因式分解】12000的因数有多少个?

1. 题目2. 枚举法求解2.1 分解质因数2.2 枚举2.3 整理分析 3. 公式求解4. 扩展4.1 因式分解4.2 因数个数 1. 题目 12000的因数有多少个&#xff1f; 2. 枚举法求解 2.1 分解质因数 2 ∣ 12000 ‾ 2 ∣ 6000 ‾ 2 ∣ 3000 ‾ 2 ∣ 1500 ‾ 2 ∣ 750 ‾ 3 ∣ 375 ‾ 5 ∣ 125 …

关于docker存储overlay2相关问题

报错如下&#xff1a; 报错原因&#xff1a;使用rm -rf 清理overlay2导致的&#xff0c;非正常清理。 正常清理命令如下&#xff1a; # 清理Docker的所有构建缓存 docker builder prune# 删除旧于24小时的所有构建缓存 docker builder prune --filter "until24h"#删…

堆中的路径

作者 陈越 单位 浙江大学 将一系列给定数字插入一个初始为空的最小堆 h。随后对任意给定的下标 i&#xff0c;打印从第 i 个结点到根结点的路径。 输入格式: 每组测试第 1 行包含 2 个正整数 n 和 m (≤103)&#xff0c;分别是插入元素的个数、以及需要打印的路径条数。下一…

20240624(周一)欧美股市总结:AI股再熄火!标普纳指连跌三日,英伟达市值跌穿3万亿美元,芯片股指跌3%

市场等待周五的美国PCE重磅通胀数据&#xff0c;今年票委、旧金山联储主席戴利称降息之前“还有更多工作要做”&#xff0c;纳指尾盘加速跳水并收跌1%&#xff0c;但道指五连涨至五周高位。谷歌、微软脱离最高&#xff0c;英伟达跌6.7%为近两个月最差&#xff0c;三天累跌近13%…

vue封装一个简单的权限管理功能, 控制页面的按钮显示与隐藏

vue封装一个简单的权限管理功能, 控制页面的按钮显示与隐藏 1、在项目入口html文件创建全局变量 <head><script>// 创建全局变量window.SITE_CONFIG {};// 登录成功后获取到的按钮权限数据存储到这里window.SITE_CONFIG[purview] [];</script> </head…

vue3-openlayers 点击多边形弹框,高亮多边形,自定义属性传递,鼠标悬浮多边形上动态修改鼠标样式

本篇介绍一下使用vue3-openlayers点击多边形弹框&#xff0c;高亮多边形&#xff0c;自定义属性传递&#xff0c;鼠标悬浮多边形上动态修改鼠标样式 1 需求 加载天地图&#xff0c;polygon传递自定义属性标悬浮在polygon上&#xff0c;根据自定义属性&#xff0c;动态修改鼠标…

AI学习指南机器学习篇-KNN的优缺点

AI学习指南机器学习篇-KNN的优缺点 在机器学习领域中&#xff0c;K最近邻&#xff08;K-Nearest Neighbors&#xff0c;KNN&#xff09;算法是一种十分常见的分类和回归方法之一。它的原理简单易懂&#xff0c;但在实际应用中也存在一些优缺点。本文将重点探讨KNN算法的优缺点…

web系统数据库敏感数据处理

一、前言 web系统数据库中保存的公民信息不允许明文存储&#xff0c;比如手机号&#xff0c;身份证号&#xff0c;收货地址等。 二、处理方式 数据库中密文存储&#xff0c;web通过注解的方式对数据加解密处理&#xff0c;下面是处理方法 1、编写接口 public interface E…

IIC学习笔记

目录 #I2C涉及相关知识 #I2C相关介绍 欢迎指正&#xff0c;希望对你&#xff0c;有所帮助&#xff01;&#xff01;&#xff01; 个人学习笔记&#xff0c;参考文献&#xff0c;链接最后&#xff01;&#xff01;&#xff01; #I2C涉及相关知识 SDA串行数据线&#xff1a; Ser…

AI降重技术:论文查重率的智能解决方案

现在大部分学校已经进入到论文查重降重的阶段了。如果查重率居高不下&#xff0c;延毕的威胁可能就在眼前。对于即将告别校园的学子们&#xff0c;这无疑是个噩梦。四年磨一剑&#xff0c;谁也不想在最后关头功亏一篑。 查重率过高&#xff0c;无非以下两种原因。要么是作为“…

软通动力携子公司鸿湖万联中标南方电网调峰调频发电有限公司数据采集装置研究开发项目

近日&#xff0c;软通动力携子公司鸿湖万联成功中标南方电网调峰调频发电有限公司数据采集装置研究开发项目。该项目是针对智能化、国产化、自主可控业务需求研制的基于国产芯片、OpenHarmony系统的抽蓄电站机械化施工数据采集设备。本次中标是软通动力布局数字能源战略的一次胜…

大数据组件--Hue

Apache Hue hue是一个集成化的大数据可视化软件&#xff0c;可以通过hue访问浏览操作主流的大数据生态圈软件。hue本身来自于cloudera后来贡献给了apachehue本身是一个web项目&#xff0c;基于python实现的&#xff0c;通过该web项目的UI集成了各个软件的UI 下面是一个hue调度…

【MySQL】如果表被锁可以尝试看一下事务

今天在MySQL中删除表的时候&#xff0c;发现无法删除&#xff0c;一执行drop&#xff0c;navicat就卡死。 通过 SHOW PROCESSLIST显示被锁了 kill掉被锁的进程后依旧被锁 最后发现是由于存在为执行完的事务 SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX; kill掉这些事务以…