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

Redis八种数据结构简介

Redis数据结构

Redis新旧版本中一共出现过八种数据结构,分别是SDS、双向链表、压缩列表、整数集合、哈希表、跳表、quicklist、listpack。

SDS

SDS是用于存储Redis中字符串的数据结构,Redis底层使用的语言是C语言,因此字符串也是C语言的字符串。然而C语言字符串存在一些问题。

1.获取长度时间复杂度为O(N),因为C语言中没有预置的字符串数据类型,而是用一个以“/0”结尾的字符数组代替的,所以要获取字符串长度的话就需要遍历整个数组,数组长度越大消耗时间就越多。

2.无法存储二进制数据,因为C语言字符串就是以"/0"结尾的字符数组,所以如果存储二进制数据的话可能会含有/0的内容,会混淆数组结尾的/0。

3.存在缓冲区溢出的风险,因为C语言不像Java等语言有自己的自动内存管理机制(如Java的垃圾回收器),而是依靠程序员手动管理内存,因此字符数组在添加内容时就可能会导致缓冲区溢出的问题。

为了解决这些问题,Redis中的字符串在原来的基础上添加了三个元数据,分别是len、alloc、flags,分别代表长度、空间长度、SDS类型。具体来说,len记录了字符串的长度,这样在获取字符串长度的时候直接从这个字段获取就行,时间复杂度为O(1),而且由于len表明了字符串的长度,因此字符串最后一位就不是必须使用“/0”结尾,所以可以存储二进制数据;

alloc则记录了字符串的空间大小,在修改字符串的时候首先会查看alloc大小是否满足,如果不满足的话会进行扩容(小于1MB翻倍,大于1MB增加1MB),这样就能避免缓冲区溢出的问题;

flags用来表示不用类型的SDS,一共有5种类型,分别是sdshdr5、sdshdr8、sdshdr16、sdshdr32和sdshdr64。这五种类型的区别在于数据结构中的len和alloc成员变量的数据类型不同,因此字符串可以用来存放不同类型的数据而不是只能为原来的字符型。

链表

由于C语言中没有链表这一数据结构,所以Redis自己实现了一个双向链表的数据结构。对于每一个节点来说,拥有前置节点、后置节点、节点值三个属性,而链表在节点的基础上封装了头结点、尾结点、节点复制函数、节点释放函数、节点比较函数、链表节点数量等属性和方法。

对链表的属性和方法中可以看出,该链表对于获取节点的上/下一个节点、头/尾节点、节点数量等操作都非常快,而且链表节点中可以保存不同类型值。

但是也有缺陷,因为链表和数组不同,内存地址并不是连续的,所以不像数组那样可以充分利用CPU缓存加速访问;而且每添加一个节点都需要为其元数据等添加额外的内存,增加了内存的开销。

压缩列表

压缩列表相对于链表来说,内存地址是连续的,和数组一样可以充分地利用CPU缓存,提高了查询的速度,而且会针对不同长度的数据进行相应编码,这种方法能有效地节省内存开销。

在Redis中,List、Hash、Zset等数据类型在包含的元素数量较少的情况下才会使用到压缩列表存储数据。

除此之外,其他和列表不同的是,压缩列表在表头有几个默认字段,分别为zlbytes(列表占用内存的字节数)、zltail(列表尾部的偏移量,可以理解为列表的容量大小)、zllen(列表包含的节点数量)、zlend(列表的结束点)。这些字段能够帮助快速获取列表大小、高效访问尾元素、元素数量等。

压缩列表中的节点也有自己的元数据,分别为prevlen(前一个节点的长度)、encoding(当前节点的数据类型和长度)、data(当前节点的实际数据)。由此可以看出不同节点的空间大小会根据其实际的数据类型进行分配,节省了内存。

连锁更新问题:由于一个节点中有prevlen属性,记录上一个节点的大小,因此当插入节点的时候,如果插入节点的下一个节点中的prevlen长度不足以标明节点的大小的时候那么就需要更新下一个节点的大小,也就是增加其prevlen属性大小,进而也就改变了下一个节点的大小,以此类推也可能改变其他节点。

尽管压缩列表能够通过连续地址和类型分配节省内存,提供一些较为高效的数据操作,但是当其所包含的元素数量过多时,由于是一片连续的内存空间,就可能导致重新分配内存地址,导致性能下降。

哈希表

Redis中的哈希表是一个数组,每个元素指向哈希表节点,哈希表节点中除了值以外还有指向下一个哈希表节点的指针,形成单向链表,因此和Java中的hashmap类似,使用链表的结构存放hash冲突的元素。

不过这里的负载因子算法是哈希表中的节点数/哈希表大小,因此当负载因子大于等于1的时候就需要进行扩容(rehash)操作。

整数集合

当一个Set中只有整数值元素的时候就会使用整数集这个数据结构作为底层实现。

当将一个新元素添加进整数集合的时候并且这个元素的长度大于整数集合中的最大元素长度时就会触发整数集合的升级,一旦升级后就无法降级。由于添加新的元素才触发升级,所以这个机制能够节省内存资源。

跳表

Redis中唯一使用到了跳表的数据类型是Zset(Zset中使用到了跳表+哈希表两种数据结构),跳表这一数据结构能够实现范围查询。

链表查询元素效率很低,而跳表在链表的基础上实现了一种多层结构,简单来说就是按照一定的跨度(两个节点之间的距离)将原来的链表进行了分层,不同的跨度能够实现跳跃式的查询。因此,跳表中存在多级索引,占用的空间较大,但是查询效率得到提升。

quicklist

在Redis3.2后,list数据类型的底层实现由原来的双向链表或压缩列表改为了quicklist,解决了由于压缩列表无法存储大量数据的问题。

quicklist的结构和链表类似,但是将链表的每个元素改为设置一个压缩列表,并且控制每个链表节点中压缩列表的大小,这样就能利用压缩列表的优势同时避免了一个压缩列表存储大量数据的问题。

在quicklist中并不是所有元素都会进行压缩,在两端处有一些数据会频繁地进行操作(像lpush、rpush、lpop、rpop等操作都是直接访问两端节点数据),因此这部分数据可以不用进行压缩,以减少性能损耗,除此之外如果有的压缩列表中只有一个元素,那也不会为之创建一个压缩列表。

quicklist允许对数据进行压缩,原理是如果数据与之前的数据重复,则只会记录重复的位置和重复的长度。

listpack

虽然quicklist降低了连锁更新的概率和造成的影响,但是没有完全避免,因为数据结构中还是用了压缩列表,因此Redis在5.0后设计了一个新的数据结构listpack来替代压缩列表,在listpack中取消了prevlen字段,避免了因为更新而可能需要不断更新相邻节点的prevlen的隐患。

listpack头也有两个属性,分别为listpack总字节数和元素数量,在尾部有结尾标识。

每个listpack节点中有len,encoding,data三个字段,其中encoding定义元素的编码类型,data为实际存放的数据,len则是encoding+data的总长度。所以listpack节点没有记录前一个节点长度,而是只记录当前节点长度,所以向listpack中加入新元素的时候不会影响其他节点,进而避免了连锁更新问题。

由于取消了prevlen字段,listpack无法像压缩列表那样进行双向遍历,但是节省了内存,避免了连锁更新。

相关文章:

Redis八种数据结构简介

Redis数据结构 Redis新旧版本中一共出现过八种数据结构,分别是SDS、双向链表、压缩列表、整数集合、哈希表、跳表、quicklist、listpack。 SDS SDS是用于存储Redis中字符串的数据结构,Redis底层使用的语言是C语言,因此字符串也是C语言的字…...

数据治理策略:确保数据资产的安全与高效利用

数据治理策略:确保数据资产的安全与高效利用 在数字化时代,数据已成为企业最宝贵的资产之一。然而,随着数据量的爆炸性增长和数据来源的多样化,如何有效地管理和利用这些数据成为企业面临的重要挑战。数据治理策略的制定和执行&a…...

ts格式转mp4,四款亲测好用软件推荐!

在这个数字视频时代,我们经常会遇到各种视频格式兼容性问题,尤其是从网络下载的高清电影或电视剧集,很多时候都是以TS格式存储。然而,当我们想要在移动设备、社交媒体或视频编辑软件中播放、上传时,MP4格式因其广泛的兼…...

10、Django Admin修改标题

admin from django.contrib import admin from .models import Category, Origin, Hero, Villain # 添加以下代码 admin.site.site_header "系统管理" admin.site.site_title "管理员界面" admin.site.index_title "欢迎来到这里&#xff…...

ESRI ArcGIS Pro 3.1.5新功能及安装教程和下载

ESRI ArcGIS Pro 3.1.5 主要新功能包括: 改进的数据编辑和管理:支持更多数据格式和更精细的属性表操作。增强的空间分析工具:新增和优化空间分析工具,提高数据分析效率。更好的3D可视化:改进3D渲染性能,支…...

人工智能,语音识别也算一种人工智能。

现在挺晚了,还是没有去睡觉,自己在想什么呢,也不确定。 这是一篇用语音写的文章,先按自己的想法说出来,然后再适当修改,也许就是一个不错的文章。 看来以后就不需要打字了,语音识别度很高&#…...

Token和Refresh Token

获取令牌(Token) 和 刷新令牌(Refresh Token) 在认证和授权机制中有不同的使用场景和目的,二者主要的区别和为什么需要刷新令牌可以通过以下几点解释: 1. 获取令牌和刷新令牌的区别 获取令牌(Token&#x…...

STM32(一)简介

一、stm32简介 1.外设接口 通过程序配置外设来完成功能 2.系统结构 3.引脚定义 4.启动配置 5.最小系统电路...

接口测试工具:Postman详解

🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 一、前言 在前后端分离开发时,后端工作人员完成系统接口开发后,需要与前端人员对接,测试调试接口,验证接口的正确性…...

Linux中全局变量配置,/etc/profile.d还是/etc/profile

全局环境变量可以放在 /etc/profile 或 /etc/profile.d/ 中,但两者的使用方式和目的有所不同: /etc/profile 作用: /etc/profile 是一个系统范围的启动脚本,在用户登录时执行。它主要用于设置全局环境变量和配置,适用于所有用户。…...

【java入门】关键字、标识符与变量初识

🚀 个人简介:某大型国企资深软件开发工程师,信息系统项目管理师、CSDN优质创作者、阿里云专家博主,华为云云享专家,分享前端后端相关技术与工作常见问题~ 💟 作 者:码喽的自我修养&#x1f9…...

Java常用类库

Java常用类库 1. **Java基础类库**1.1 **java.lang**1.2 **java.util**1.3 **java.io**1.4 **java.nio**1.5 **java.time** 2. **第三方类库**2.1 **Apache Commons**2.2 **Google Guava**2.3 **Jackson**2.4 **Lombok** 3. **Spring相关类库**4. **并发类库**4.1 **java.util.…...

2024年高教社杯数学建模国赛C题超详细解题思路分析

本次国赛预测题目难度,选题人数如下所示 难度评估 A:B:C 1.8:1.3:1 D:E1.5:1 选题人数 A:B:C 1:1.5:2.8 D:E0.5:1.2 C题一直以来都是竞赛难度最低、选题人数最多的一道本科生选题,近三年C题的选题人数一直都是总参赛队伍的一半左右,2023年…...

深度学习(七)-计算机视觉基础

计算机视觉 计算机视觉在广义上是和图像相关的技术总称。包括图像的采集获取,图 像的压缩编码,图像的存储和传输,图像的合成,三维图像重建,图像增强,图像修复,图像的分类和识别,目…...

机器人笛卡尔空间轨迹规划原理与MATLAB实现

机器人笛卡尔空间轨迹规划是指在给定的笛卡尔坐标系(通常是三维空间坐标系)中规划机器人的末端执行器(如抓手、焊枪等)的移动路径。这种规划方式直观且易于理解,因为它直接关联到任务空间中机器人的位置和姿态。下面将…...

数据结构:树与二叉树

1、树的基本概念 1.1树的定义 树是n个结点的有限集。 若n0,称为空树;若n>0称为非空树,非空树有且仅有一个称之为根的结点。 除根结点以外的其余结点可分成m个互不相交的有限集T1,T2,......Tm,每个有限集合本身又是一棵树,并…...

BUUCTF—[网鼎杯 2020 朱雀组]phpweb

题解 打开题目是这样子的。 啥也不管抓个包看看,从它返回的信息判断出func后面的是要调用的函数,p后面的是要执行的内容。 那我们直接执行个系统命令看看,可以看到返回了hack,估计是做了过滤。 funcsystem&pls 直接读取源码…...

什么是CDN及其如何影响SEO?

有没有想过,为什么你的网站在谷歌搜索结果的后几页徘徊,即使你已经优化了每一个网页? 有时候, 慢速的网站性能可能是罪魁祸首。 如果这个问题引起了你的共鸣,那么你可能想要探索一下内容分发网络(Content…...

python实现粒子群算

博客目录 引言 什么是粒子群算法(PSO)?粒子群算法的应用场景为什么使用粒子群算法? 粒子群算法的原理 粒子群算法的基本概念粒子位置和速度的更新规则粒子群算法的流程粒子群算法的特点与优势 粒子群算法的实现步骤 初始化粒子群…...

【Unity案例】搭建射击系统与UI

上期将基础的移动系统搭建完毕后就可以开始搭建更加复杂的系统部分了 前排提示,由于一开始仅思考如何完成操作相关功能,以至于到后面重构稍微有些困难,继续写下去恐成屎山,故在搭完射击和武器UI后不再继续泛化到敌人和敌人状态机…...

vscode里如何用git

打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...

【JavaSE】多线程基础学习笔记

多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...

群晖NAS如何在虚拟机创建飞牛NAS

套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...

go 里面的指针

指针 在 Go 中,指针(pointer)是一个变量的内存地址,就像 C 语言那样: a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10,通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...

C++--string的模拟实现

一,引言 string的模拟实现是只对string对象中给的主要功能经行模拟实现,其目的是加强对string的底层了解,以便于在以后的学习或者工作中更加熟练的使用string。本文中的代码仅供参考并不唯一。 二,默认成员函数 string主要有三个成员变量,…...

深入浅出WebGL:在浏览器中解锁3D世界的魔法钥匙

WebGL:在浏览器中解锁3D世界的魔法钥匙 引言:网页的边界正在消失 在数字化浪潮的推动下,网页早已不再是静态信息的展示窗口。如今,我们可以在浏览器中体验逼真的3D游戏、交互式数据可视化、虚拟实验室,甚至沉浸式的V…...