Redis的哈希表是如何扩容的?

news2024/12/22 19:57:39

文章目录

  • 一般面试回答
  • 哈希表结构
  • 字典数据结构
  • 解决哈希冲突
  • 扩容/缩容
    • 对字典的哈希表rehash步骤
  • 渐进式rehash
    • 渐进式rehash步骤
  • 相关问题

一般面试回答

redis 解决冲突的方法是使用链地址法,另外当容量不足的时候,则使用Rehash 进行扩容。
Rehash:
给哈希表 2 分配更大的空间,
例如是当前哈希表 1 大小的两倍;
把哈希表 1 中的数据重新映射并拷贝到哈希表 2 中;
释放哈希表 1 的空间。
渐进式rehash则是不一次性拷贝,当访问到某个数据时,再进行拷贝。

哈希表结构

Redis哈希表就是类似Java中HashMap。
在这里插入图片描述
哈希表是由dictht结构体定义,table是一个数组,数组中每个元素是一个指向dictEntry结构的指针。
在这里插入图片描述
dictEntry是哈希表的节点,每个节点保存着一个键值对key、value,value可以是一个指针、或者是一个unit64_t或int64_t整数。
next属性则是一个指向另一个哈希表节点的指针,形成一个链表,主要也是为了解决哈希冲突问题。
比如下图(一个空的哈希表):
在这里插入图片描述
索引值相同的两个节点使用链表连接起来:
在这里插入图片描述

字典数据结构

提到哈希表的话顺便了解以下字典数据结构,毕竟它的底层就是哈希表实现的。
在这里插入图片描述
type:指向dictType指针,保存了操作特定类型键值对的函数,Redis为不同用途的字典设置不同的类型特定函数。
privdata:保存了需要传递给不同特定函数的可选参数。
ht[2]:两个哈希表,字典使用的哈希表是ht[0],ht[1]则是当对ht[0]哈希表进行rehash时使用。
trehashidx:记录当前rehash进度,没有进行rehash则为-1。
在这里插入图片描述
普通状态下的字典:
在这里插入图片描述

解决哈希冲突

将一个键值对添加到字典时,需要计算其哈希值和索引值,接着根据索引值将新节点放到哈希表数组的指定索引上。
计算哈希值:
hash = dict->type->hashFunction(key)
计算索引值:
hash & dict->ht[x].sizemask
键冲突:当两个或以上数量的键进行哈希之后索引值一样,也就是说两个节点要放在同一个同桶里,这时可能就会存在覆盖(冲突),那么就得解决这种冲突了。
Redis解决键冲突的方法:链地址法(separate chaining)——拉链法,假设你已了解Java HashMap原理,这里链地址法原理就不细说了。
解决哈希冲突有哪些方法?

  • 再哈希法
  • 链地址法
  • 开放地址法
  • 建立公共溢出区

扩容/缩容

为什么要进行扩容或缩容?
扩容原因:当hashtable存储的元素过多,可能由于碰撞也过多,导致其中某链表很长,最后致使查找和插入时间复杂度很大。因此当元素超多一定的时候就需要扩容。
缩容原因:当元素数量比较少的时候就需要缩容以节约不必要的内存。为了让哈希表的负载因子(load factor)维持在一个合理的范围内,会使用rehash(重新散列)操作对哈希表进行相应的扩展或收缩。负载因子的计算公式:哈希表已保存节点数量 / 哈希表大小
load_factor = ht[0].used / ht[0].size
扩容条件(满足任意一个即可)

  • Redis服务器目前没有在执行BGSAVEBGREWRITEAOF命令,并且哈希表的负载因子大于等于1。
  • Redis服务器目前在执行BGSAVEBGREWRITEAOF命令,并且哈希表的负载因子大于等于5。

为什么BGSAVEBGREWRITEAOF命令是否在执行,Redis服务器哈希表执行扩容所需的负载因子不相同(1或5)?
BGSAVE:用于在后台异步保存当前数据库的数据到磁盘。
BGREWRITEAOF:用于异步执行一个 AOF( Append Only File ) 文件重写操作。
因为当执行BGSAVEBGREWRITEAOF命令过程中,Redis需要创建服务器进程的子进程,操作系统采用的是COW,即 写时复制 copy-on-write的技术来优化子进程的使用效率。所以在子进程存在时,服务器会提高执行扩容所需的负载因子,从而尽可能避免在子进程存在期间进行扩容,可以避免不必要的内存写入操作,最大限度节约内存

缩容的条件:哈希表的负载因子小于0.1。

对字典的哈希表rehash步骤

  • 为ht[1]分配空间:扩展操作,那么ht[1] 的大小为第一个大于等于ht[0] .used*2的2的n次幂;收缩操作,那么ht[1] 的大小为第一个大于等于ht[0].used 的2的n次幂
  • 将ht[0]中的数据转移到ht[1]中,在转移的过程中,重新计算键的哈希值和索引值,然后将键值对放置到ht[1]的指定位置。
  • 当ht[0]的所有键值对都迁移到了ht[1]之后(ht[0]变为空表),将ht[0]释放,然后将ht[1]设置成ht[0],最后为ht[1]分配一个空白哈希表:

开始哈希前:
在这里插入图片描述
为ht[1]分配空间,ht[0].used当前值为4,8恰好是第一个大于等于4的2的N次幂,那么当前就会将ht[1]哈希表大小设置为8。
在这里插入图片描述
将ht[0]的键值对都rehash到ht[1]
在这里插入图片描述
释放ht[0],将ht[1]设置为ht[0],ht[1]再设置为空的哈希表
在这里插入图片描述

渐进式rehash

为什么要进行渐进式rehash?
在元素数量较少时,rehash会非常快的进行,但是当元素数量达到几百万、甚至几个亿时进行rehash将会是一个非常耗时的操作。如果一次性将成万上亿的元素的键值对rehash到ht[1],庞大的计算量可能会导致服务器在一段时间内停止服务,这是非常危险的!所以,rehash这个动作不能一次性、集中式的完成,而是分多次、渐进式地完成。

渐进式rehash步骤

  • 为ht[1]分配空间,让字典同时持有ht[0]和ht[1]两个哈希表。
  • 在字典中维持一个索引计数器变量rehashidx,并将它的值设置为0,表示rehash工作正式开始
  • 在rehash进行期间,每次对字典执行CRUD:添加、删除、查找或者更新操作时,程序除了执行指定的操作以外,还会顺带将ht[0]哈希表在rehashidx索引上的所有键值对rehash到ht[1],当rehash工作完成之后,程序将rehashidx+1(表示下次将rehash下一个桶)。
  • 随着字典操作的不断执行,最终在某个时间点上,ht[0]的所有键值对都会被rehash至ht[1],这时程序将rehashidx属性的值设为-1表示rehash完成。

渐进式rehash的好处:在于它采取分而治之的方式,将rehash键值对所需的计算工作均摊对字典的每个添加、删除、查找和更新操作上,从而避免了集中式rehash 而带来的庞大计算量

准备进行渐进式rehash:
在这里插入图片描述
此时rehashidx=0,也就是会将索引为0的键值对进行迁移
在这里插入图片描述
接着,rehashidx继续递增,假如到最后将索引3的进行迁移
在这里插入图片描述
渐进式rehash结束

在这里插入图片描述
在迁移过程中,会不会造成读少数据?
不会,因为在迁移时,首先会从ht[0]读取数据,如果ht[0]读不到,则会去ht[1]读。
在迁移过程中,新增加的数据会存放在哪个ht?
迁移过程中,新增的数据只会存在ht[1]中,而不会存放到ht[0],ht[0]只会减少不会新增。

相关问题

Redis的字典渐进式扩容与ConcurrentHashMap的扩容策略比较?那么他们在扩容、CRUD时有什么区别呢?

时间对比:
一个单线程渐进扩容,一个多线程协同扩容。在平均的情况下,是ConcurrentHashMap快。这也意味着,扩容时所需要
花费的空间能够更快的进行释放。
读操作:
两者的性能相差不多。
写操作:
Redis的字典返回更快些,因为它不像ConcurrentHashMap那样去帮着扩容(当要写的桶位已经搬到了newTable时),等扩容完才能进行操作,而redis则是直接将新加的元素添加到ht[1]
删除操作:
和写操作一样。

如果内存资源吃紧,希望能够进行快速的扩容方便释放扩容时需要的辅助空间,选择多线程协同扩容。
如果对于写和删除操作要求迅速,那么可以选择单线程渐进扩容。

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

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

相关文章

96-Linux_UDP实现客户端和服务器端

UDP实现客户端和服务器端 一.udp实现客户端和服务器端的编程流程二.udp实现实现客户端和服务器端所用的接口1.socket2.sendto3.recvfrom 三.udp服务器端代码和客户端的代码1.服务器端2.客户端 一.udp实现客户端和服务器端的编程流程 udp提供无链接的,不可靠的,数据报服务; 二…

burp suite 插件编写-基础

文章目录 前言一、插件的官方文档二、Montoya API jar包结构三、HTTP 处理程序小结 四、代码示例 前言 burp插件入门。入门,我们大概有一个框架,心里不再有怎么做,为什么可以这么做的疑问。现在就要更具体的来回答“怎么做”这个问题。我们通…

基于C#编程建立泛型Vector数据类型及对应处理方法

目录 一、简介 二、方法 2.1 建立Vector类 2.2 Vector成员 2.3 Vector属性 2.4 Vector方法 2.4.1 构造函数 2.4.2 Vector元素操作方法 2.4.3 Vector 运算 三、调用方法 3.1 方法 3.1.1 Append 3.1.2 this[] 3.1.3 Insert 3.1.4 DelLen 3.1.5 FindNumber 3.1.6 …

PHP快速入门04-前后端数据交互与文件上传

文章目录 前言前后端数据交互与文件上传前后端数据交互 $_GET $_POST文件上传 总结 前言 本文已收录于PHP全栈系列专栏:PHP快速入门与实战 前后端数据交互与文件上传 前后端数据交互 $_GET $_POST 他们都是超全局变量。它们用于从HTTP请求中获取数据&#xff0…

除了学历,你更需要有能力

遥想当年,家里培养出一个大学生,是多荣耀的事!可现今却处于一个比较尴尬的状态。 为什么大学生贬值得这么厉害?其实大学生之所以会不值钱不外乎三大原因:量大、与企业需求不匹配、质量差。 高校扩招下,大…

OpenAI最新官方ChatGPT聊天插件接口《接入插件快速开始》全网最详细中英文实用指南和教程,助你零基础快速轻松掌握全新技术(二)(附源码)

Getting started 快速开始 前言Introduction 导言Plugin manifest 插件清单OpenAPI definition OpenAPI定义Running a plugin 运行插件Setup a local proxy of your public API 设置公共API的本地代理 Writing descriptions 书写描述Best practices Debugging 排除故障其它资料…

【Android】popup windows 的使用方式 和 遇到不显示的坑

背景 在项目开发过程中有一个需求就是点击一个问号icon 弹出相关提示信息在下面,那么就得对这个做适配了。 计划采用popupWindow 实现: 参考 实现 基本的套路就是写一个xml对应的布局,然后在java 层使用即可。 特别注意的是该xml布局要慎…

ubuntu输入法问题汇总

Xfce4桌面环境输入法 Ubuntu20.04、ubuntu21.04中安装xfce4桌面环境,自带中文输入法; 原生xubuntu20.04中文输入法问题解决办法: 更新语言支持失败的话,终端键入:sudo apt-get install cmake qt5-default qtcreator…

网络安全——传输层安全协议(3)

作者简介:一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭:低头赶路,敬事如仪 个人主页:网络豆的主页​​​​​​ 目录 前言 一.SSL密钥更改协议 二.SSL告警协议 关闭报警 错误报警 三.SSL协议安全性…

软件测试测试开发技能

从事软件测试许多年,想必很多人都有感到迷茫不知所措的时候,人生的十字路口有很多,该如何抉择呢?有人成功转型,QA、项目管理、配置管理。当然还有技术型,性能测试、自动化测试、测试开发,而想要…

爬虫JS逆向思路-hook钩子

网络上几千块都学不到的JS逆向思路这里全都有👏🏻👏🏻👏🏻 本系列持续更新中,三连关注不迷路👌🏻 干货满满不看后悔👍👍👍 &…

连续3天3场分享,KubeVela@KubeCon EU 2023 抢鲜看!

自从 2019 年 Open Application Model 诞生以来,KubeVela 已经经历了几十个版本的变化,并向现代应用程序交付先进功能的方向不断发展。最近,KubeVela 完成了向 CNCF 孵化项目的晋升,标志着社区的发展来到一个新的里程碑。今天&…

有了MySQL,为什么还要有NoSQL

🏆今日学习目标: 🍀MySQL和NoSQL的区别 ✅创作者:林在闪闪发光 ⏰预计时间:30分钟 🎉个人主页:林在闪闪发光的个人主页 🍁林在闪闪发光的个人社区,欢迎你的加入: 林在闪闪…

PYQT5学习笔记02——程序基本结构之面向对象版本

我们之前写的代码耦合性比较高,复用性不高,这是面向过程编程的缺点。我们可以把程序基本结构设计成面向对象的版本,把 设置控件这部分内容封装到一个类中。 比如这个是我们的窗口,红色的矩形是相同的菜单控件,既然是一…

研报精选230420

目录 【行业230420浙商证券】卫浴行业深度报告:智能卫浴新变局,国货崛起正当时 【行业230420平安证券】氢能全景图(上)制氢篇:商业模式起步,绿氢初试锋芒 【行业230420天风证券】建筑装饰行业深度研究&…

InstructGPT:Training language models to follow instrcutions with human feedback

InstructGPT:Training language models to follow instrcutions with human feedback 介绍模型数据集TaskHuman data collectionmodel 实验结果参考 介绍 现在LLM可以被prompt来完成一系列的下游任务,然而这些模型也总会产生一些用户不想要的结果&#…

ESP32 WiFi扫描、WiFi通道查询

ESP32WiFi扫描程序 代码解决什么问题? 扫描周围WiFi并识别指定WiFi名称的WiFi通道(为了ESP32的esp-now协议正常通信)。 这跟ESP32 now有什么关系? ESP32使用NOW协议进行通信时,要求参与通信的设备必须处于同一物理…

ASEMI代理ADI亚德诺AD8603AUJZ-REEL7车规级芯片

编辑-Z AD8603AUJZ-REEL7芯片参数: 型号:AD8603AUJZ-REEL7 偏移电压:12μV 偏移电压漂移:1μV/C 输入偏置电流:0.2 pA 输入失调电流:0.1 pA 输入电压范围:–0.3 to 5.2V 输入电容&#…

Win10+VS2019安装vcpkg

vcpkg是一个C的包管理器。类似Python中的pip。安装后可以直接通过命令下载一些C的包,省的自己编译配路径。安装后的包都被vcpkg统一保存,统一配置路径。VS中哪个项目需要,就将其配置到某个项目中,当然也可以全局配置。 1、下载vc…

Node 04-http模块

HTTP 协议 概念 HTTP(hypertext transport protocol)协议;中文叫 超文本传输协议 是一种基于TCP/IP的应用层通信协议 这个协议详细规定了 浏览器 和 万维网 服务器 之间互相通信的规则 协议中主要规定了两个方面的内容: 客户端&#xff1…