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

redis面试(三)Hash数据结构

HASH

哈希,在redis底层实现的时候,数据的结构叫做dict

这个Dict就是一个用于维护key和value映射关系的数据结构,与很多语言中的Map类型相似。

本质上也是一个数组+链表的形式存在,不同的点在于,每个dict中是可以存在两个(数组+链表)形式的哈希表。

就是为了在扩容的时候避免一次性对所有的key进行重哈希计算操作,而是将重哈希操作分散到之后每次的增删改查中。 这样一来,每次重哈希的时候也不影响单个请求,与redis“快速相响应时间”的设计原则是相符的。

结构

dict为了实现增量式重哈希,在数据结构里面包含两个哈希表。重哈希期间,也就是扩容时候,将数据从第一个哈希表向第二个哈希表迁移。

包含的结构类型有三种,从大到小的结构类型依次是:
dict -> dictht -> dictEntry

具体的结构定义如下:

dict

哈希本身的包装类,也就是我们操作的Hash

typedef struct dict {dictType *type; // 类型特性的函数,保存了操作特定类型kv的函数void *privdata;	// 私有数据,传递给类型特定函数的可选的参数dictht ht[2];	// 哈希表,两个dictht哈希表,默认就用一个,另外一个是在rehash(扩容)的时候用的,ht[0]->在使用的hashtable,ht[1]->备用的hashtableint trehashidx;	// rehash索引,默认是-1,因为没有进行rehash
}

dictht

真正的hash表

typedef struct dictht {dictEntry **table;	// 数组,放我们的一个一个的key-value对,就是放在数组里unsigned long size; // 数组大小unsigned long sizemask; // 掩码,根据key hash计算数组里面的索引值,size-1unsigned long used; // 已有的节点数量
} dictht;

dictEntry

key-value 键值结构对,并且里面包含链表的指针

typedef struct dictEntry {void *key; // key值union { // value值void *val;uint64_tu64;int64_ts64;
} v;struct dictEntry *next; // 指向下一个节点,单向链表结构,解决hash冲突问题的
} dictEntry;

dictType

这个结构严格上来说,并不是dict本身数据结构的一部分,而是一些封装好的操作方法

typedef struct dictType {// hash方法,根据关键字计算哈希值unsigned int (*hashFunction)(const void *key);// 复制keyvoid *(*keyDup)(void *privdata, const void *key);// 复制valuevoid *(*valDup)(void *privdata, const void *obj);// 关键字比较方法int (*keyCompare)(void *privdata, const void *key1, const void *key2);//  销毁keyvoid (*keyDestructor)(void *privdata, void *key);// 销毁valuevoid (*valDestructor)(void *privdata, void *obj);} dictType;

结构总结

  • 一个dict中,会包含两个dictht(dict hash table)
  • dictht中会包含所有的 dictEntry(键值对)
  • 两个dictht在扩容的时候会用到第二个

hash计算

hash = dict->tpe->hashFunction(key); // Mermerhash2,是hash值的计算算法
index = hash & dict->ht[0].sizemask; // hash值跟hashtable的sizemask掩码,做一个二进制与运算,算出来的结果,一定是数组->size->范围内的一个数字,掩码=size-1,算出来一定是最大也就是size-1

hash冲突

冲突是hash表常见的一个问题,数组大小是固定的,不同的key,key多个,不同的,但是不同的key算出来的hash值也是不同的,不同的hash值算出来的数组里的index位置,有可能是一样的

但是一直往里面写入key的时候,很可能会有一个问题,那就是不同的key,但是算出来的index索引是一样的,这就是key冲突问题了,这个时候就得基于dictEntry的单向链表,来挂载一个位置的多个key了

数组扩容

如果要是放的数据太多了,数组放不下了,这就得扩容,扩容的时候,每个key都得重新计算索引位置了,这就是rehash,扩容的时候,ht[1]就是新的一个哈希表,他的大小是第一个大于等于ht[0].used * 2的2n次方,如果是缩容,那就是第一个大于等于ht[0].used的2n次方

ht[0].used = 15 * 2 = 30,找到第一个大于等于30的2n次方,2的5次方=32,32就是我们的下一次要扩容的数组大小

搞了一个新hash表以后,就可以把ht[0]里的所有key都rehash到ht[1]里了,崇安计算索引值,解决key冲突问题,全部迁移完毕之后,就会释放掉ht[0]了,把ht[1]设置为ht[0],然后在ht[1]创建空的hash表

rehash扩容缩容的触发

如果说同一个位置,他的元素的个数实在是太多了,单向链表太长了,hget的时候,key,hash,index,一下子发现这个数组那里,那个位置,单向链表实在是太长了,O(n)时间复杂度,去遍历单向链表,一个一个去寻找这个key

老hashtable放的数据太多了,多到一定程度了,就会触发rehash,多到多少才会去触发rehash呢?
触发rehash是跟负载因子有关系的,这个负载因子计算公式是,已有节点数量/哈希表数组大小,就是load_factor = ht[0].used / ht[0].size,当我们的负载因子达到一定的程度的时候,就会触发rehash动作

如果要是redis没有执行rdb和aof的持久化动作,redis负载不会太高,可控一些,此时一直到负载因子为1,就进行rehash扩容,也就是数组满了再说;但是如果rdb和aof再执行动作,这个时候只要负载因子超过5,这意思就是说,rdb和aof执行的过程中,你就别扩容了

rdb和aof执行的时候,是要开子进程来执行的,很耗费内存和cpu,所以这个时候,就要避免进行hash表扩容了,此时无非就是说,很多写入都是用单向链表挂再一个位置就行了,没问题的

渐进式迁移

如果负载因子小于0.1了,就要对hash表进行收缩操作,扩容也好,缩容也把,对于ht[1]的新size的计算公式,都给大家讲过了,当你创建好了一个ht[1]之后,迁移,渐进式的迁移机制来做的

另外就是说,在rehash的时候,如果hash表里数据太多了,你要一下子执行rehash,会导致负载太高,对性能有影响,所以此时不是一下子全部进行rehash的,一下子rehash的数量太多了,会导致负载很高,redis服务器的性能,不对的,是渐进式的,分批次,逐步的执行rehash动作

hashidx默认是-1,改为0以后就开始rehash了,每次执行hash表操作,就会把index=n的所有元素迁移到ht[1]里去,然后hashidx++,这样随着你不停的执行hash表操作,就会逐步的把ht[0]各个索引位置的元素迁移到ht[1]里去了,一直到所有元素都迁移完毕,然后就会把rehashidx改为-1

在rehash期间,新的hash操作,都是针对ht[1]写入的,同时迁移ht[0],在读取的时候,会在ht[0]里找一下,找不到再去ht[1]里找,这就ok了

负载因子触发rehash,新数组的size计算公式,rehashidx渐进式迁移

相关文章:

redis面试(三)Hash数据结构

HASH 哈希,在redis底层实现的时候,数据的结构叫做dict 这个Dict就是一个用于维护key和value映射关系的数据结构,与很多语言中的Map类型相似。 本质上也是一个数组链表的形式存在,不同的点在于,每个dict中是可以存在…...

Java基础语法

注释 注释就是在程序指定位置添加的说明性信息 简单理解,就是对代码的一种解释 注释有三种: 单行注释 格式://注释信息 多行注释 格式:/*注释信息*/ 文档注释 格式:/**注释信息*/ 注释的注意事项…...

Qt | QChart+QChartView+QLineSeries(折线图)+QBarSeries(柱状图)实战

点击上方"蓝字"关注我们 01、QLineSeries QLineSeries 是 Qt 中的一个类,用于在图表中表示一系列的数据点。它继承自 QAbstractSeries 类,提供了绘制折线图所需的基本功能。 常用的方法包括 append(x, y):向序列中添加一个新的数据点,其中 x 和 y 分别表示横坐…...

公布一批脸书爬虫(facebook)IP地址,真实采集数据

一、数据来源: 1、这批脸书爬虫(facebook)IP来源于尚贤达猎头公司网站采集数据; ​ 2、数据采集时间段:2023年10月-2024年7月; 3、判断标准:主要根据用户代理是否包含“facebook”和IP核实。…...

Package.Json 参数配置理解用途

"dev": "SET NODE_OPTIONS--openssl-legacy-provider & vue-cli-service serve --open" 这行命令首先设置环境变量NODE_OPTIONS,添加了--openssl-legacy-provider标志。这个标志用于解决某些情况下Node.js在Windows系统上使用OpenSSL时可能…...

k3:增加触发器,当外协单和报料单新增时,更新生产任务单的“说明”栏

外协单新增时 CREATE TRIGGER [dbo].[t_BOS257800018Entry2_update]ON [dbo].[t_BOS257800018Entry2]AFTER insert AS BEGINSET NOCOUNT ON; ------实现当外协时,生产任务单的说明有标识(240731 BY WK) declare fid_souce as int; declare…...

神奇海洋养鱼小程序游戏广告联盟流量主休闲小游戏源码

在海洋养鱼小程序中,饲料、任务系统、系统操作日志、签到、看广告、完成喂养、每日签到、系统公告、积分商城、界面设计、拼手气大转盘抽奖以及我的好友等功能共同构建了一个丰富而互动的游戏体验。以下是对这些功能的进一步扩展介绍: 饲料 任务奖励&a…...

分享几个适合普通人的AI副业变现思路

最近很多人问:看你做AI也做了有一两年了,也没见有什么产出啊?其实很多事情是长期主义,并不是一时半会儿马上就看到收益了。 正如董宇辉出名前也只是新东方默默无闻的一位老师,李佳琪曾经也只是一个化妆品销售。抱着长…...

如何使用CANoe自带的TCP/IP Stack验证TCP的零窗口探测机制

如果想利用CANoe自带的TCP/IP协议栈验证TCP的零窗口探测机制,就必须添加一个网络节点并配置独立的CANoe TCP/IP协议栈,作为验证对象。而与它进行TCP通信的对端也是一个网络节点,但不要配置TCP/IP协议栈,而是使用CAPL代码在底层组装TCP报文模拟TCP通信过程。这样可以尽量减少…...

二进制搭建 Kubernetes v1.20(中)

一、部署 CNI 网络组件 目录 一、部署 CNI 网络组件 1.flannel简介 1)UDP模式 2)VXLAN 模式 2.部署flannel ​编辑 3.Calico简介 1.flannel简介 K8S 中 Pod 网络通信:●Pod 内容器与容器之间的通信 在同一个 Pod 内的容器&#xff0…...

Scrapy 爬取旅游景点相关数据(七):利用指纹实现“不重复爬取”

本期学习: 利用网页指纹去重 众所周知,代理是要花钱的,那么在爬取(测试)巨量网页的时候,就不可能对已经爬取过的网站去重复的爬,这样会消耗大量的时间,更重要的是会消耗大量的IP (金…...

java的对象向上转型

对象向上转型,父类对象就可以调用子类重写父类的方法,这样当父类对象需要添加新的功能时,只需要添加一个子类,在子类中对父类的功能进行扩展,而不需要更改父类代码 向上转型,格式如下 父类类型 父类对象子…...

Navicat Premium 16破解

Navicat Premium 16破解教程 1安装Navicat Premium 16 通过百度网盘分享的文件:Navicat_Premium_16_chs-x64.zip 链接:https://pan.baidu.com/s/1ryRSJ2d9s6rXI09LEmLtpw?pwdz7wo 提取码:z7wo 一直下一步即可 2破解 选择刚才安装路径&am…...

【C/C++】C语言到C++的入门知识点(主要适用于C语言精通到Qt的C++开发入门)

【C/C】C语言到C的入门知识点(主要适用于C语言精通到Qt的C开发入门) 文章目录 C语言与C的不同C中写C语言代码C语言到C的知识点Qt开发中需要了解的C基础知识namespace输入输出字符串类型class类构造函数和析构函数(解析函数)类的继…...

docker 建木 发版 (详细教程)

先创建git仓库 Git勤勉 两种方式上传-CSDN博客 把项目送上去 进入建木 可以接着这个来 dockerfile部署镜像 ->push仓库 ->虚拟机安装建木 ->自动部署化 (详细步骤)-CSDN博客 创建分组项目 开始操作 git 上钩子 前面链接里有这个教…...

什么样的人适合学习网络安全?

一、引言 在当今数字化的时代,网络安全已经成为了一个至关重要的领域。随着网络攻击的日益频繁和复杂,对于网络安全专业人才的需求也在不断增长。然而,并不是每个人都适合学习网络安全。那么,究竟什么样的人适合投身于这个充满挑…...

大厂linux面试题攻略四之Linux网络服务(二)

五、Linux网络服务-Apache优化 1.请写出工作中常见的Apache优化策略 Apache服务器优化是提升网站响应速度和稳定性的重要手段。在工作中,常见的Apache优化策略包括以下几个方面: 1. 启用压缩技术 Gzip压缩:使用Gzip压缩技术可以减少服务器…...

MySQL和PostgreSQL group by后 Concatenate 拼接所有的字符串

MySQL: GROUP_CONCAT(DISTINCT t2.T_CODES ORDER BY t2.T_CODES ASC) AS t_str, PostgreSQL 8.4 array_to_string(array_agg(t2.T_CODES), , ) AS t_str, PostgreSQL 9 string_agg(t2.T_CODES), , )...

Python爬虫技术 第24节 数据清洗和预处理(二)

在Python爬虫项目中,数据清洗和预处理是非常关键的步骤。这部分工作通常涉及到字符串操作、缺失值处理和数据格式转换等方面。下面我将详细讲解这些方面的内容,并提供具体的代码示例。 1. 字符串操作 字符串操作在数据清洗过程中非常重要,因…...

conda常用命令整理

Anaconda是一个流行的Python和R编程语言的开源发行版,用于科学计算和数据分析。它包含了许多常用的开源软件包和工具,适用于数据科学、机器学习、大数据处理和科学计算等领域。Anaconda的核心是conda。conda是一个包管理器和环境管理器,可以轻…...

【Python】 -- 趣味代码 - 小恐龙游戏

文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包

文章目录 现象&#xff1a;mysql已经安装&#xff0c;但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时&#xff0c;可能是因为以下几个原因&#xff1a;1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

均衡后的SNRSINR

本文主要摘自参考文献中的前两篇&#xff0c;相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程&#xff0c;其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt​ 根发送天线&#xff0c; n r n_r nr​ 根接收天线的 MIMO 系…...

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…...