Redis面试题系列:讲一讲 rehash 的过程

2024-04-09 20:58

本文主要是介绍Redis面试题系列:讲一讲 rehash 的过程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

字典是什么

字典,又称为符号表(Symbol table),关联数组(associative array)或映射(map),是一种用来保存键值对(key-value-pair)的抽象数据结构。字典中的键不会重复。
接下来会分析Redis中字典的实现方式,哈希算法,解决键冲突的方法及rehash的过程。文中展示的 Redis 源码均来自 3.0.4 版本。

字典的实现

Redis 的字典使用哈希表作为底层实现,一个哈希表里面可以有多个结点,每个结点保存了一个键值对。

typedef struct dictht {// hash表结点数组// 每个 table[i] 其实是一个链表的头节点dictEntry **table;// hash表结点数组的大小,总是为 2^nunsigned long size;// 用于计算索引值的掩码,总是等于 size-1unsigned long sizemask;// 该hash表中的结点数量unsigned long used;
} dictht;

table 是一个数组,数组中每个元素其实都是一个链表的头指针。链表中每个结点都保存着一个键值对。
size 属性记录了table数组的大小,Redis的扩容和收缩机制,保证了 size 总是为 2^n。
sizemask 是用于计算索引值的掩码,总是等于 size-1。
used 记录了哈希表中结点的数量,即所有链表中结点的总数。

哈希算法

当要将一个新的键值添加到字典里面时,程序会先先根据键值对的键计算出哈希值和索引值,然后再根据索引值,将包含新键值对的节点放到哈希表数组(table)的指定索引上面。
Redis 通常使用 MurmurHash2 计算键的哈希值。该算法由 Austin Appleby 于 2008 年发明,这种算法的优点在于,即使输入的键是有规律的,算法仍能给出一个很好的随机分布性,并且算法的计算速度也非常快。
而索引值计算则非常简单:将哈希值和 dictht::sizemask 做与运算的结果即为索引值。
比如,哈希值为 6,sizemask 为 3,则索引值为 6&3 = 2。

解决键冲突

当有两个或以上数量的键被分配到了同一个索引上面时,我们称这些键发生了冲突。比如上图中 k2 和 k0。
Redis 使用链地址法解决冲突。每个节点都有一个 next 指针,多个冲突的结点通过 next 指针构成一个单向链表,这样就解决了键冲突的问题。

Rehash

负载因子:哈希表中单向链表的平均长度。

随着增删操作的进行,Redis 通过 rehash 操作将负载因子维持在一个合理的范围内。Rehash操作分为两种:

  • 扩展:当负载因子较大时,应该扩大 dictht::size 以降低平均长度,加快查询速度。
  • 收缩:当负载因子较小时,应该减小 dictht::size 以减少对内存的浪费。
typedef struct dict {//哈希表dictht ht[2];//rehashidx 记录了rehash 的进度。//当没有进行 rehash 时为 -1。int rehashidx; // 其他数据成员....
};

rehash 过程如下:

  • 为字典的ht[1]哈希表分配空间,ht[1].size 的大小取决于要执行的操作,以及ht[0].used 的值。
    • 如果执行的是扩展操作:那么 ht[1].size 为最小的且不小于 ht[0].used*2 的 2 的 n 次方。比如 ht[0].size 为 5,那么 ht[1].size 为 16。
    • 如果执行的是收缩操作:那么 ht[1].size 为最小的且不小于 ht[0].used 的 2 的 n 次方。比如 ht[0].size 为 5,ht[1].size 为 8。
  • 将 ht[0] 中所有键值对移动到 ht[1] 中:根据 ht[1].sizemask 重新计算哈希值与索引值;根据新的索引值将键值对插入到 ht[1] 中;将键值对从 ht[0] 中删除。
  • 当 ht[0] 中所有键值对移动到 ht[1] 之后开始执行清理工作:释放 ht[0] 占用的内存;将 ht[1] 赋值给 ht[0];为 ht[1] 分配一个空的哈希表,为下一次 rehash 做准备。
    在这里插入图片描述

渐进式 rehash

扩展或收缩哈希表需要将 ht[0] 的所有键值对移动到 ht[1] 当中。这个动作是分多次,渐进式地完成的。原因在于当键值对过多时,一次性移动所有键值对会导致Redis在一段时间内无法对外提供服务
渐进式 rehash 步骤如下:

  • 为 ht[1] 分配空间,此时字典同时存在两个哈希表。
  • 将 dict::rehashidx 置为 0,rehash 工作正式开始。
  • 在 rehash 进行期间,每次对字典执行增删改查操作时,程序在执行指定操作之外,还会将 ht[0] 在 rehashidx 索引上的所有键值对rehash 到 ht[1],然后将 rehashidx 的值加一。
  • 随着字典操作的不断执行,ht[0] 的所有键值对最终会全部移动到 ht[1],此时程序会将 rehashidx 设为 -1,表示 rehash 操作已完成。

特别的,在渐进式 rehash 操作过程中,因为同时存在两个哈希表,所以字典的删除,查找,更新操作会在两个哈希表上进行。程序会先尝试在 ht[0] 中寻找目标键值对,如果没有找到则会在 ht[1] 再次进行寻找,然后进行具体操作。但是新增操作只会在 ht[1] 上进行,这保证了 ht[0] 中的已经被清空的单向链表不会新增元素。

这篇关于Redis面试题系列:讲一讲 rehash 的过程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/889200

相关文章

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

作业提交过程之HDFSMapReduce

作业提交全过程详解 (1)作业提交 第1步:Client调用job.waitForCompletion方法,向整个集群提交MapReduce作业。 第2步:Client向RM申请一个作业id。 第3步:RM给Client返回该job资源的提交路径和作业id。 第4步:Client提交jar包、切片信息和配置文件到指定的资源提交路径。 第5步:Client提交完资源后,向RM申请运行MrAp

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

荣耀嵌入式面试题及参考答案

在项目中是否有使用过实时操作系统? 在我参与的项目中,有使用过实时操作系统。实时操作系统(RTOS)在对时间要求严格的应用场景中具有重要作用。我曾参与的一个工业自动化控制项目就采用了实时操作系统。在这个项目中,需要对多个传感器的数据进行实时采集和处理,并根据采集到的数据及时控制执行机构的动作。实时操作系统能够提供确定性的响应时间,确保关键任务在规定的时间内完成。 使用实时操作系统的

flume系列之:查看flume系统日志、查看统计flume日志类型、查看flume日志

遍历指定目录下多个文件查找指定内容 服务器系统日志会记录flume相关日志 cat /var/log/messages |grep -i oom 查找系统日志中关于flume的指定日志 import osdef search_string_in_files(directory, search_string):count = 0

一些其他面试题

阿里二面:那你来说说定时任务?单机、分布式、调度框架下的定时任务实现是怎么完成的?懵了。。_哔哩哔哩_bilibili 1.定时算法 累加,第二层每一个格子是第一层的总时间400 ms= 20 * 20ms 2.MQ消息丢失 阿里二面:高并发场景下引进消息队列有什么问题?如何保证消息只被消费一次?真是捏了一把汗。。_哔哩哔哩_bilibili 发送消息失败