Redis数据结构SDS,IntSet,Dict

目录

1.字符串:SDS

1.1.为什么叫做动态字符串

2.IntSet

2.1.inset如何保存大于当前编码的最大数字?

3.Dict

3.1Dict的扩容

3.2Dict的收缩

3.3.rehash


1.字符串:SDS

SDS的底层是C语言编写的构建的一种简单动态字符串 简称SDS,是redis比较常见的数据结构。

由于以下几种缺点,Redis并没有直接采用C语言的字符串。

1.获取长度需要计算

2.非二进制安全 :中间不能有 \0,否则就中断。

3.不可修改 :char * s = "hello"  

在这里Redis会在底层的创建两个SDS,一个是 “key” 的SDS ,一个是value。

由于不能直接采用C语言字符串,所以采用了一个结构体

注意:len的比特位数每一位就是单位是字节

举例:对于name字符串来说,redis底层的SDS结构体中,长度是4字节,向内存申请的字节数是4字节,flags则表示不同的编码格式

编码格式有什么作用?

核心目的是优化内存使用 和 操作效率 对于不同长度的字符串可以使用不同的头结构。

1.1.为什么叫做动态字符串

因为它具备动态扩容能力,好比一个 “hy”字符串的SDS

len:2alloc:2flags:1hy\0

如果要给它追加字符”Boy“,那么它首先会申请新的内存空间

如果新的字符串空间 < 1M,那么新空间是扩展后的2倍+1

如果新的字符串空间 > 1M,那么新空间是扩展后字符串长度 + 1M + 1

这个就是内存预分配。

len:5alloc:11flags:1hyBoy\0

优点:

1.获取字符串的长度时间复杂度为O(1)

2.支持动态扩容

3.二进制安全(中间可以有\0存在)

4.减少内存分配次数


2.IntSet

IntSet是Redis中集合的一种实现方式,基于整数数组来实现,并且具备长度可变,有序的特征。

encoding包含三种工作模式,表示储存的整数大小不同:

分别可储存2字节整数,4字节整数,8字节整数。

如果IntSet中储存三个数那么它的储存结构应该是这样的:采用默认的encoding,每个数字2字节

length大小是元素的个数,contents数组保存元素。

2.1.inset如何保存大于当前编码的最大数字?

当encoding采用int16_t 也就是2个字节大小来存放每个数字,所以每个数字不应该超过两个字节,最大是32767。当存下99999时会升级编码的方式去找到合适大小.

如图是升级编码前后的contents数组占用空间大小的情况

1.升级编码到INT_32,每个数字占4字节

2.依次将每个元素向后拷贝到正确的位置

3.将要添加的元素放入末尾

4.最后将encoding的属性改为INT_32,length的值更新。

源码:

intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {uint8_t valenc = _intsetValueEncoding(value);// 获取当前值编码uint32_t pos; // 要插入的位置if (success) *success = 1;// 判断编码是不是超过了当前intset的编码if (valenc > intrev32ifbe(is->encoding)) {// 超出编码,需要升级return intsetUpgradeAndAdd(is,value);} else {// 在当前intset中查找值与value一样的元素的角标posif (intsetSearch(is,value,&pos)) {if (success) *success = 0; //如果找到了,则无需插入,直接结束并返回失败return is;}// 数组扩容is = intsetResize(is,intrev32ifbe(is->length)+1);// 移动数组中pos之后的元素到pos+1,给新元素腾出空间if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);}// 插入新元素_intsetSet(is,pos,value);// 重置元素长度is->length = intrev32ifbe(intrev32ifbe(is->length)+1);return is;
}
static intset* intsetUpgradeAndAdd(intset* is, int64_t value) {// 获取当前intset编码uint8_t curenc = intrev32ifbe(is->encoding);// 获取新编码uint8_t newenc = _intsetValueEncoding(value);// 获取元素个数int length = intrev32ifbe(is->length);// 判断新元素是大于0还是小于0 ,小于0插入队首、大于0插入队尾int prepend = value < 0 ? 1 : 0;// 重置编码为新编码is->encoding = intrev32ifbe(newenc);// 重置数组大小is = intsetResize(is, intrev32ifbe(is->length) + 1);// 倒序遍历,逐个搬运元素到新的位置,_intsetGetEncoded按照旧编码方式查找旧元素while (length--) // _intsetSet按照新编码方式插入新元素_intsetSet(is, length + prepend, _intsetGetEncoded(is, length, curenc));/* 插入新元素,prepend决定是队首还是队尾*/if (prepend)_intsetSet(is, 0, value);else_intsetSet(is, intrev32ifbe(is->length), value);// 修改数组长度is->length = intrev32ifbe(intrev32ifbe(is->length) + 1);return is;
}

总结:

1.InSet确保元素唯一,有序

2.具备类型升级,可以节省空间

3.底层采用二分查找来查询元素

4.如果数组过长,扩容时移动元素效率不高


3.Dict

Redis是一种键-值型的数据库,那么键和值间关系的维护就要靠Dict维护。

Dict是由三部分组成,分别是哈希表(DictHashTable) ,哈希节点(DictEntry),字典(Dict)

当向Dict添加键值对时,Redis首先根据key计算哈希值(h),然后利用 h & sizemask(图上掩码)计算出元素应放索引的位置,假设哈希值 h = 1, 则 1 & 3 = 1, 因此储存到角标1位

一个Dict包含两个hash表,其中一个一般是空,rehash才使用,比如负载因子超过阈值,扩展,收缩,初始化时使用。

3.1Dict的扩容

DIct中的HashTable就是数组结合单项链表实现,当集合中的元素过多必然导致hash冲突,同时链表过长hash的查询效率也会越来越低。

每次新增键值都会查询负载因子(LoadFactor = used / size)

1.如果LoadFactor >= 1 同时服务器没有执行BGSAVE/BGREWRITEAOF等后台进程。

2.哈希表的LoadFactor > 5,此时就算后台执行进程也会强制扩容。

扩容的大小为used + 1,以下是扩容操源码

static int _dictExpandIfNeeded(dict *d){// 如果正在rehash,则返回okif (dictIsRehashing(d)) return DICT_OK;    // 如果哈希表为空,则初始化哈希表为默认大小:4if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE);// 当负载因子(used/size)达到1以上,并且当前没有进行bgrewrite等子进程操作// 或者负载因子超过5,则进行 dictExpand ,也就是扩容if (d->ht[0].used >= d->ht[0].size &&(dict_can_resize || d->ht[0].used/d->ht[0].size > dict_force_resize_ratio){// 扩容大小为used + 1,底层会对扩容大小做判断,实际上找的是第一个大于等于 used+1 的 2^nreturn dictExpand(d, d->ht[0].used + 1);}return DICT_OK;
}

首先判断是否正在rehash 是就返回ok,否则向下执行。如果哈希表是空的就会初始化4个单位,

如果负载因子 >= 1 ,并且没有进行后台进程 或者 负载因子>= 5. 开始进行扩容。

3.2Dict的收缩

DIct除扩容之外还可以 收缩,每次执行删除后如果 负载因子 < 0.1 就会执行。

3.3.rehash

不管是扩容还是收缩,每次变化都会导致size 和 sizemask(掩码)变化,而key的查询与sizemask有关。所以必须对哈希表中每一个key重新计算索引,插入新的哈希表,这个过程就是rehash。

1.重新计算hash表的realeSize,值取决于当前要做的是扩容还是收缩

扩容:则新的size是第一个 >= dict.ht[0].used + 1 的 2^n

删除:则新的size是第一个 >= dict.ht[0].used 的 2^n (不小于 4)

2.按照新的realeSize申请内存空间,创建dictht,并赋值给dict.ht[1]

3.设置dict.rehashidx = 0,标识开始rehash

4.将dict.ht[0]的每个dictEntry都rehash到dict.h1

5.将dict.ht[1]的值赋给dict.ht[0] ,给dict.ht[1]初始化为 空哈希表,释放原来的dict.ht[0]的内存。

rehash不是一次能够完成的,如果数据百万entry则需要分批次完成,否则可能导致线程阻塞

所以叫渐进式rehash

1.重新计算hash表的realeSize,值取决于当前要做的是扩容还是收缩

扩容:则新的size是第一个 >= dict.ht[0].used + 1 的 2^n

删除:则新的size是第一个 >= dict.ht[0].used 的 2^n (不小于 4)

2.按照新的realeSize申请内存空间,创建dictht,并赋值给dict.ht[1]

3.设置dict.rehashidx = 0,标识开始rehash

4.将dict.ht[0]的每个dictEntry都rehash到dict.h1

5.每次增删查改都检查dict.rehashidx是否 > -1,如果是则将dict.ht[0].table[rehashidx]的entry链表rehash到dict.ht[1],并且将rehashidx++,直到所有数据都rehsh到dict.ht[1]

6.将dict.ht[1]的值赋给dict.ht[0] ,给dict.ht[1]初始化为 空哈希表,释放原来的dict.ht[0]的内存。

7.rehashidx赋值-1,代表rehash结束

8.在rehash过程中,新增操作,则直接写入ht[1],查询、修改和删除则会在dict.ht[0]和dict.ht[1]依次查找并执行。这样可以确保ht[0]的数据只减不增,随着rehash最终为空

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

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

相关文章

Maven的聚合工程与继承

目录 一、为什么需要使用Maven工程 二、聚合工程的结构 三、聚合工程实现步骤 四、父工程统一管理版本 五、编译打包 大家好&#xff0c;我是jstart千语。想着平时开发项目似乎都是用maven来管理的&#xff0c;并且大多都是聚合工程。而且在maven的聚合工程中&#xff0c…

前端职业发展:如何规划前端工程师的成长路径?

前端职业发展:如何规划前端工程师的成长路径? 大家好,我是全栈老李。今天咱们聊聊前端工程师的职业发展路径,这个话题看似简单,实则暗藏玄机。就像打游戏升级一样,你得知道下一关是什么,才能提前准备装备和技能点。 前端之路 一般我们从一个新手到大神,普遍需要经过…

【星海出品】分布式存储数据库etcd

etcd 数据库由 CoreOS 公司创建。 https://github.com/etcd-io/etcd api信息 https://etcd.io/docs/v3.5/dev-guide/api_reference_v3/ etcdctl --help etcd 最初由 CoreOS 公司开发&#xff0c;作为其核心项目之一。 CoreOS 成立于 2013 年&#xff0c;专注于容器化技术&#…

2025新版修复蛇年运势测试风水起名系统源码

2025新版修复蛇年运势测试风水起名系统源码 通过网盘分享的文件&#xff1a;2025xbfsysweb.rar 链接: https://pan.baidu.com/s/1r1MOkJJJMj9s9nQX_GzI3Q 提取码: 9weh 备用下载地址&#xff1a;http://pan.1234f.com:5212/s/JK1uw

Vue3 Pinia

一、Pinia 核心概念 Pinia 是 Vue3 官方推荐的状态管理库&#xff0c;相比 Vuex 4&#xff0c;具有以下优势&#xff1a; 更简洁的 API&#xff08;移除 mutations&#xff09; 完整的 TypeScript 支持 支持组合式 API 自动代码分割 轻量级&#xff08;仅 1KB&#xff09;…

音视频小白系统入门课-4

本系列笔记为博主学习李超老师课程的课堂笔记&#xff0c;仅供参阅 往期课程笔记传送门&#xff1a; 音视频小白系统入门笔记-0音视频小白系统入门笔记-1音视频小白系统入门笔记-2音视频小白系统入门笔记-3 将mp4文件转换为yuv文件 ffmpeg -i demo.mp4 # 输入文件-an …

6.2 内容生成与营销:个性化内容创作与营销策略优化

随着消费者对个性化体验的需求日益增长&#xff0c;传统的内容创作与营销方式已难以满足市场竞争的需要。基于大语言模型&#xff08;LLM&#xff09;与智能代理&#xff08;Agent&#xff09;的技术为企业提供了全新的解决方案&#xff0c;能够实现高效、精准、规模化的内容生…

kafka课后总结

Kafka是由LinkedIn开发的分布式发布 - 订阅消息系统&#xff0c;具备高吞吐量、低延迟、可扩展性、持久性、可靠性、容错性和高并发等特性。其主要角色包括Broker、Topic、Partition、Producer、Consumer、Consumer Group、replica、leader、follower和controller。消息系统中存…

DataStreamAPI实践原理——计算模型

引入 通过前面我们对于Flink的理解&#xff0c;我们知道它吸收了 Dataflow 的理念&#xff0c;以及此前已有的流处理系统&#xff08;如 S4、Storm、MillWheel&#xff09;的经验&#xff0c;实现了批流一体化的高效数据处理&#xff0c;并且通过灵活的窗口机制、事件时间与水…

项目笔记1:通用 Service的常见方法

通用 Service 通常封装了常见的业务逻辑操作&#xff0c;以提高代码的复用性和可维护性。不同的框架和业务场景下&#xff0c;通用 Service 的方法会有所差异&#xff0c;但一般都会包含一些基本的增删改查&#xff08;CRUD&#xff09;操作&#xff0c;以下为你详细介绍&#…

阿里云99机器总是宕机,实测还是磁盘性能差

阿里云99计划总是宕机&#xff0c;经过反复排查&#xff0c;最终确认还是磁盘性能差。 阿里云99机器使用的磁盘类型是Entry云盘40GiB (2120 IOPS) 按照官方的一些数据&#xff0c;这个磁盘最小iops是1800最大是6000,实际使用中发现&#xff0c;这个6000值很虚&#xff0c;这个…

Fedora 43 计划移除所有 GNOME X11 相关软件包

Fedora 43 计划移除所有 GNOME X11 相关软件包&#xff0c;这是 Fedora 项目团队为全面拥抱 Wayland 所做的重要决策。以下是关于此计划的详细介绍&#xff1a; 提案内容&#xff1a;4 月 23 日&#xff0c;Neal Gompa 提交提案&#xff0c;建议从 Fedora 软件仓库中移除所有 G…

魔幻预言手游》:职业介绍!

在《魔幻预言》手游中&#xff0c;共有武玄、魔魅、剑仙三大核心职业&#xff0c;各具特色且定位鲜明&#xff0c;以下为具体介绍&#xff1a; 一、武玄&#xff08;战士&#xff09; 核心定位&#xff1a;近战物理输出与团队增益担当&#xff0c;兼具控制与防御能力。 战斗风…

精益数据分析(27/126):剖析用户价值与商业模式拼图

精益数据分析&#xff08;27/126&#xff09;&#xff1a;剖析用户价值与商业模式拼图 在创业和数据分析的领域中&#xff0c;每一次深入学习都是一次成长的契机。今天&#xff0c;我们继续秉持共同进步的理念&#xff0c;深入研读《精益数据分析》&#xff0c;剖析用户价值的…

【SwitchyOmega安装教程】

目录 一、插件安装 1. 下载安装文件 2. 打开浏览器扩展安装页面 3. 安装插件 二、界面详情 三、配置信息 3.1 设置IP 1、查看IP地址信息 2、批量测试IP是否有效 3、点击扩展程序&#xff0c;选择 Proxy SwitchyOmega 4、 点击选项进行配置 5、配置页面 一、插件安装 1…

矫平机终极指南:特殊材料处理、工艺链协同与全球供应链管理

一、特殊材料矫平&#xff1a;挑战与创新解决方案 1. 高温合金&#xff08;如Inconel 718&#xff09;处理 技术难点&#xff1a; 屈服强度高达1100 MPa&#xff0c;传统矫平力不足 高温下易氧化&#xff0c;需惰性气体保护环境 解决方案&#xff1a; 采用双伺服电机驱动&a…

反事实——AI与思维模型【82】

一、定义 反事实思维模型是一种心理认知模型,它指的是人们在头脑中对已经发生的事件进行否定,然后构建出一种可能性假设的思维活动。简单来说,就是思考“如果当时……,那么就会……”的情景。这种思维方式让我们能够超越现实的限制,设想不同的可能性和结果,从而对过去的…

Nginx:支持 HTTPS

文章目录 Nginx 开启 ssl 以支持 HTTPS1 生成本地证书2 开启 ssl 以支持 HTTPS3 将 https 的请求转发给 http 最终的 nginx.conf 如下 Nginx 开启 ssl 以支持 HTTPS [!IMPORTANT] 在下文中&#xff0c;将采用如下定义。 HTTP端口&#xff1a; 80 HTTPS端口&#xff1a; 443 服务…

[计算机科学#2]:从继电器到晶体管的电子计算机发展史(庞然大物的进化)

【核知坊】&#xff1a;释放青春想象&#xff0c;码动全新视野。 我们希望使用精简的信息传达知识的骨架&#xff0c;启发创造者开启创造之路&#xff01;&#xff01;&#xff01; 内容摘要&#xff1a;本文讲述了20世纪初至1950年代计算机技术的发展历程…

【ESP32S3】Cache 框图和操作

ESP32-S3 采用双核共享 ICache (指令缓存) 和 DCache &#xff08;数据缓存&#xff09; 结构&#xff0c;如下图所示。以便当 CPU 的指令总线和数据总线同时发起请求时&#xff0c;也可以迅速响应&#xff1a; Cache 的存储空间与内部存储空间可以复用。具体为 Internal SRAM0…