Redis数据结构

字节青训营后端打卡笔记,主题结构参照文章,以及网络上其它很多的资料所记录下来的笔记。

Redis数据结构一览

SDS(Simple Dynamic String)

C语言字符串的缺陷

  1. 获取字符串长度函数strlen()时间复杂度为O(N)

  2. 字符串以\0结尾,意味着字符串里的内容不能包括\0字符

  3. 字符串不会记录自身缓冲区大小,因此对字符串的操作函数不安全,容易造成缓冲区溢出,有可能导致程序运行终止

SDS结构设计

  • len,记录了字符串的长度,因此获取字符串长度的时间复杂度为O(1)

  • alloc,分配给字符数组的空间长度。在修改字符串时,可以通过alloc-len来计算出剩余的空间大小,可以用来判断空间是否满足修改需求。如果不满足,就会自动将SDS的空间扩展至执行修改所需的大小,然后才执行实际的修改操作。见下图:

    请添加图片描述

    需要注意的是,s_realloc函数在动态扩容时,如果检测到原来内存空间后面一段连续空间长度未能满足要求,仍旧会申请新的内存空间并把原来的数据复制过去。根据chatGPT,这取决于程序对内存空间的分配,是很不确定的,而且大概率是都要新申请空间并复制数据到新的内存空间

  • flags,用来表示不同类型的SDS。一共5种,sdshdr5、sdshdr8、sdshdr16、sdshdr32 和 sdshdr64,就是2的次方。

  • buf[],字符数据,用来保存实际数据

链表

链表结点结构设计

typedef struct listNode {//前置节点struct listNode *prev;//后置节点struct listNode *next;//节点的值void *value;
} listNode;

可以看到这是一个双向链表

链表结构设计

typedef struct list {//链表头节点listNode *head;//链表尾节点listNode *tail;/*//节点值复制函数void *(*dup)(void *ptr);//节点值释放函数void (*free)(void *ptr);//节点值比较函数int (*match)(void *ptr, void *key);*///链表节点数量unsigned long len;
} list;

其中,dup、free、match函数是可以自定义实现的功能函数,暂且不管。只需要关注链表的结构就行。

链表的优势与缺陷

优点

  • 从某个结点获取其前缀与后缀结点的时间复杂度是o(1)

  • 获取链表的头结点与尾结点的时间复杂度也是O(1)

  • 获取链表长度的时间复杂度也是O(1),因为维护了长度len

  • 每个链表结点使用void*保存指针结点的值,因此可以保存各种不同类型的值

缺点

  • 内存不连续,因此无法快速定位某一个结点

  • 保存值需要一个链表结点结构头的分配,内存开销较大

压缩列表

结构设计

由连续内存块组成的顺序型数据结构,其结构如下:

  • zlbytes,记录整个压缩列表占用对内存字节数

  • zltail,记录压缩列表列表尾的偏移量

  • zllen,记录压缩列表包含的结点数量

  • zlend,标记压缩列表的结束点,固定值0xFF(十进制255)

结点包含三部分:

  • prevlen,记录了[前一个结点的长度]

  • encoding,记录了当前结点实际数据的类型以及长度

  • data,记录数据

prevlen

prevlen表示前一个结点的长度,以便能够从后向前遍历列表。它的编码规则如下:

  1. 如果前一个结点长度小于254,那么就占用一个字节表示前一个结点的长度

  2. 如果前一个结点长度大于等于254,那么第一个字节为254,再额外使用4个字节(等价于一个int)表示前一个结点的长度。

encoding

encoing编码代表了当前结点的实际数据的类型以及长度,其编码规则简写如下:

  1. 当前结点的数据是整数,使用1字节空间编码

  2. 当前结点数据是字符串,根据字符串的长度大小,使用1字节/2字节/5字节的空间进行编码

具体编码规则可以网上搜索。只要记住它代表了当前结点的实际数据的类型以及长度就行了。

连锁更新

压缩列表充分利用了内存进行存储,并通过prevlenencoding来记录前一个结点的长度以及当前结点实际数据类型以及长度。

基于以上特点,当压缩列表修改某一数据时,如果发生了数据类型的变化,或者数据长度发生了较大变化,很可能需要往后占用额外的内存空间。于是不仅该结点需要更新,其之后的所有结点都需要更新,这就叫“连锁更新”。

例如:

这同时也是压缩列表的缺陷之一。这也意味着压缩列表不适合用于存储较多的数据

哈希表

Redis中的哈希表就是很常规的哈希表。

当产生哈希冲突时,将同一哈希值的数据用一个链表连起来以避免哈希冲突。其他没有什么结构上的特殊之处了。

rehash

值得一提的是,Redis中使用了双哈希表的模式来支持rehash操作,其结构如下:

在正常服务请求阶段,插入的数据都会写入到[哈希表1],此时的[哈希表2]并不会被分配空间。

当触发rehash时,有以下三个步骤:

  1. 给[哈希表2]分配空间,一般会比[哈希表1]大2倍

  2. 将[哈希表1]的数据迁移到[哈希表2]中

  3. 迁移完成后,[哈希表1]的空间会被释放,并把[哈希表2]设置为[哈希表1],然后再[哈希表2]处新创建一个空白的哈希表,为下次rehash做准备

这其中产生的问题,在第二步过程中,如果[哈希表1]的数据量非常大,那么在迁移到[哈希表2]的时候,会涉及到大量的数据拷贝,此时可能会对Redis造成阻塞,于是就有了渐进式rehash

渐进式rehash

  • 在rehash期间,每次哈希表元素进行新增、删除、查找或者更新操作时,Redis除了执行对应的操作之外,还会将该索引位置上的所有key-value迁移到[哈希表2]上,直到rehash结束,继续之后的步骤。

这里其实不太严谨,根据chatGPT,准确地说,旧表会被标记为“过期”且不会再接受插入的操作。这也就意味着删除、查找以及更新会同时在两张表中进行,如果是查找,按照优先度会先在表2中查找,没找着再在表1中查找

rehash触发条件

通过负载因子来判断是否需要rehash。

触发rehash的条件主要有两个,我现在还不理解,还需要学习Redis更加深入,这里先挖个坑:

  • 当负载因子大于等于 1 ,并且 Redis 没有在执行 bgsave 命令或者 bgrewiteaof 命令,也就是没有执行 RDB 快照或没有进行 AOF 重写的时候,就会进行 rehash 操作。

  • 当负载因子大于等于 5 时,此时说明哈希冲突非常严重了,不管有没有有在执行 RDB 快照或 AOF 重写,都会强制进行 rehash 操作。

整数集合

整数集合是Set对象的底层实现之一。整数集合本质上是一块连续内存空间,它的结构定义如下:

typedef struct intset {//编码方式uint32_t encoding;//集合包含的元素数量uint32_t length;//保存元素的数组int8_t contents[];
} intset;

整数集合的升级

当一个元素加入到整数集合中,如果新元素(例如int32_t)比整数集合现有的所有元素(int16_t)都要长时,整数集合需要先进行升级,按新元素的类型扩展contents数组的空间大小,然后才能将新元素加入到整数集合里。其流程见下图:

升级的好处

  • 提升灵活性。通过自动升级底层数组来适应元素,可以将任意类型的整数添加进集合中,而不必担心出现内存错误

  • 节约内存。只会在有需要的时候升级,因此能够尽量节约存储空间。例如,当元素全是int16_t时,不会升级数据使用int64_t存储,只有当存在int64_t的数据时,才会对数组进行升级。

跳表

跳表(SkipList)是一个有序的链表,操作效率可以达到O(logN)。

跳表的特点

  • 多层链表结构

  • 每一层都是有序链表,并且按照元素升序(默认)排列

  • 元素出现在哪一层是随机决定的,采用的是随机从0层开始,有p的概率加一层的方法(比如添加在第二层的概率是 p^2*(1-p) ),在原论文中给出的p0.25,意味着期望层数是\frac{1}{1-p}=1.33。chatGPT表示一般p设置为0.5,期望层数为2。

  • 只要元素出现在了第k层,那么k层以下的链表也会出现这个元素。

  • 底层包含所有元素。

  • 头尾结点不存储元素,且头尾结点的层数就是跳表的最大层数,如果元素随机到的层数超过最大层数,则扩展最大层数再添加该元素

  • 跳表中的结点包含两个指针,一个指针指向同层链表的后一个结点,另一个指向下层链表的同元素结点

网络上传播较广的跳表结点结构如下:

typedef struct zskiplistNode {//Zset 对象的元素值sds ele;//元素权重值double score;//后向指针struct zskiplistNode *backward;//节点的level数组,保存每层上的前向指针和跨度struct zskiplistLevel {struct zskiplistNode *forward;unsigned long span;} level[];
} zskiplistNode;

这个结构和跳表最后一个特点有点不一样,它没有指向下层链表的同元素结点,作为替代,用一个数组实现了同元素的不同层,同时用span代替level,表示该元素在该层上往后最远能跨多少个元素

在每个结点上,还存在一个后向指针,用于从后往前遍历。

查找过程

上图为查找元素71的过程:

  1. 从头结点的最高层开始找

  2. 对于到达的每个结点(包括头结点),如果当前权重小于要查找的权重时,则访问该层上的下一个结点

  3. 如果该层没有下一个结点,则访问该结点上的低一级层

  4. 如果该层下一个结点大于要查找元素权重,则访问该结点上的低一级层

quicklist

quicklist其实就是双向链表+压缩链表,但是添加一个元素时,不会像普通链表那样,直接新建一个链表结点,而是会检查插入位置的压缩列表能否容纳该元素,如果能容纳就直接保存到quicklistNode结构里的压缩列表,如果不能容纳,才会新建一个quicklistNode结构。

quicklist会控制 quicklistNode 结构里的压缩列表的大小或者元素个数,来规避潜在的连锁更新的风险,但是这并没有完全解决连锁更新的问题

listpack(替换压缩列表)

压缩链表由于会保存前一个结点的长度,所以会引发连锁更新的隐患。于是发明了listpack来替代压缩列表。

  • encoding,定义该元素的编码类型,会对不同长度的整数和字符串进行编码;

  • data,实际存放的数据;

  • len,encoding+data的总长度;

由于不会再记录前一个字段的长度,因此不会产生连锁更新。

关于压缩列表与listpack的问题

我曾有过这样的疑问,压缩列表和listpack如果都在前面插入元素,那后面的结点所在的地址不是都要整体往后挪,也即需要新的内存吗?我问了chatGPT,结果如下:

也就是说,如果没超过预先给其分配的内存空间,只需要整体往后挪,相对地址发生变化即可,不需要重新分配内存。

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

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

相关文章

手抖把Python2.7卸载了,导致了自己的yum不可用以及yum因python版本无法使用的问题

摘要: 从标题就能看到我有多心如死灰了,简单介绍下我是如何自残的过程. ①首先因为需要部署爬虫程序,然后安装Python3. ②Python3系列和Python2系列版本不向下兼容,所以我就卸载了机器自带的Python2.7,删的干干净净. ③然后我下载了Python3.8的包. ④我开始使用yum命令安装…

LangChain+LLM大模型问答能力搭建与思考

1. 背景 最近,大模型(LLMs,Large Language Models)可谓是NLP领域,甚至整个科技领域最火热的技术了。凑巧的是,我本人恰好就是NLP算法工程师,面临着被LLMs浪潮淘汰的窘境,决定在焦虑…

给AI挖坑 | 实测New Bing能否回答员工那些刁钻的问题?

ChatGPT狂飙160天,世界已经不是之前的样子。 新建了人工智能中文站https://ai.weoknow.com 每天给大家更新可用的国内可用chatGPT资源 之前玩ChatGPT,发现这玩意很擅长胡说八道,比如你让它写一段发言稿,并引用鲁迅名言&#xff0…

如何用Rosetta全家桶设计一个抗体?

新冠肆虐无药可医, 医护冒险奋战在前线。 实验室里抗体设计, 试管里混合液波光粼粼, 分子结构、细胞实验频频。 日以继夜,孜孜不倦, 只为破解疫情的难题。 我们紧密团结,努力前行, 心中不灭的信…

【ChatGPT 】《ChatGPT 后续:我开发了一个超级阅读器,免费分享给大家》- 知识点目录

《ChatGPT 后续:我开发了一个超级阅读器,免费分享给大家》 00:00 我们开发了超级阅读器 01:37 思路和开发过程 03:00 使用方式 03:43 AI 工具加持开发效率 04:14 更多可能性 04:57 局限性 1. 介绍:PandaGPT 上传文献聊天窗口提问 2. DALL…

搭建正版GPT4.0!不用ChatGPT账号,不要API!

手把手教你免费搭建正版GPT4.0!不用ChatGPT账号,不要API! 项目简介 项目地址:https://github.com/ramonvc/freegpt-webui优点: 完全免费且不需要任何 API 密钥 ❌🔑 该项目的特点是使用G4F API 的WebUI …

他做了一个「ChatGPT 杀手」,a16z 抢着投

比「GPT 侦探」更重要的是,AI 生成内容在不同行业的「容忍度」。 图片来源:由无界版图AI工具生成 作者 | 美漪编辑 | 靖宇 最近两个月,科技圈最热的话题,无疑是 OpenAI 推出的对话式 AI 应用 ChatGPT,不仅可以让它给你…

巴比特 | 元宇宙每日必读:ChatGPT「代码解释器」正式解禁,它补齐了ChatGPT的哪些短板?用户该如何使用?...

摘要:7月9日,OpenAI 的聊天机器人 ChatGPT 推出了新功能:代码解释器(Code Interpreter)。这个新功能已经对所有 Plus 订阅用户开放,其扩展了 ChatGPT 的功能,为用户带来了更好的交互式编程体验和…

ChatGPT应用组队学习来了!

Datawhale学习 联合主办:Datawhale、百度文心 Datawhale联合百度文心,五月为大家带来AIGC应用专题:大模型从入门到应用,学习大纲如下(文末整理了这次学习的所有资料): 参与学习 ▶ 活动时间&am…

阿尔法狗咬向ChatGPT七寸

图片来源:由无界AI生成 瞄准ChatGPT,谷歌的下一枚炮弹已经准备好,只待发射。而担负起发射任务的,是谷歌DeepMind。 昨天,谷歌DeepMind的CEO德米斯哈萨比斯(Demis Hassabis)在采访中放出豪言&…

谷歌版ChatGPT突然公测!上手实测结果在此,体验申请通过飞快

杨净 金磊 发自 凹非寺量子位 | 公众号 QbitAI 谷歌吃了大亏之后,这次一声不吭,放了大招: 对标ChatGPT的Bard测试版,刚刚正式对外发布。 而且这次用户在申请候补名单之后,无需经历漫长的等待时间。 没错,量…

对抗 ChatGPT 的创业武器:专注和紧密的反馈循环

ChatGPT 超越谷歌主导地位 在我的上一篇文章中,我探讨了 ChatGPT 超越谷歌主导地位的可能牛市案例。但我也对我认为是熊市的情况表示赞赏。正如我提到的,ChatGPT 的无界界面有点像,而不是 DoorDash 的重点推出策略,DoorDash 在美国所有城市和商品类别中同时推出,当你订购…

chatgpt赋能Python-python_queque

Python Queue模块实现队列的介绍 Python语言是一种通俗易懂、功能丰富的编程语言。它的标准库还包括许多有用的模块,用于实现各种数据结构和算法。其中,Queue模块是一种实现队列的模块。这个模块实现了多线程编程时所必需的队列数据结构。 什么是队列&…

ChatGPT已能操控机器人,工程师连代码都不用写,网友:微软在搞天网?

Alex 发自 凹非寺量子位 | 公众号 QbitAI 当我还在跟ChatGPT吹牛尬聊时,有人已经在拿它操控机器人了。 不是别人,正是OpenAI的金主爸爸、不久前刚拿ChatGPT“重新发明搜索引擎”的微软。 到目前为止,开发者调教机器人不仅技术门槛高&#xff…

火爆外网的ChatGPT,改Bug,敲代码不在话下

目录 前言 一、ChatGPT 是什么? 二、ChatGPT到底有什么用 1.可以回答问题 2.帮你创作文章和标题 3.调试代码和修复代码 4.检测安全漏洞,也许还能创建PoC 总结 前言 这几天ChatGPT AI 可谓是火的一塌糊涂,那么它到底是什么&#xff1f…

Python使用itchat库+图灵机器人(新手上路)

前不久有个朋友说,谁谁的男朋友写个机器人,然后聊天很嗨的样子,看下面图,然后今天下午闲着,就把整理了下思路,采用Python进行如下开发,具体步骤如下: 1、第一步,因为我是…

图灵 | 计算机器与智能

【“计算机器与智能”选自《Mind》,no.2236(1950.10),P433-460。牛津大学出版社允许重印。刘西瑞、王汉琦 翻译】 1. 模仿游戏 我建议来考虑这个问题 :“机器能够思维吗?” 这可以从定义 “机 器” 和 “思…

图灵奖得主LeCun评ChatGPT不算创新,被网友骂柠檬精

“ChatGPT并不算创新。” “OpenAI做的这个东西跟其他实验室相比,根本算不上什么进步。” 这两天,图灵奖得主LeCun公开和大热趋势“唱反调”,瞬间引发网友围观。 要知道,ChatGPT功能强大又好玩,火爆全网,任…

本地化部署大语言模型 ChatGLM

本地化部署大语言模型 ChatGLM 本地化部署大语言模型 ChatGLM前期筹备GitHub 基础包语言模型文件基础配置显存查看方法 Anaconda 模块ChatGLM-6B 网页部署Anaconda 环境创建根目录操作基础依赖加载transformers 和 protobuf 库加载Pytorch 源修改依赖库补充补充依赖 pypi 配置c…

麻将AI 不完全信息博弈学习笔记(完结)

前言 在这学期的数据结构必修课中,老师向我们提供了两道题: 其一是六子棋问题; 其二是麻将AI问题; 前者是经典的完全信息博弈问题,根据我已有的知识,利用博弈树和合理的剪枝可以提供一种高效的解法&#x…