Redis核心数据结构之字典(二)

字典

解决键冲突

当有两个或以上数量的键被分配到了一个哈希表数组的同一个索引上面,我们称这些键发生了冲突(collision)。
Redis的哈希表使用链地址法(separate chaining)来解决键冲突,每个哈希表节点都有一个next指针,多个哈希表节点可以用next指针构成一个单向链表,被分配到同一个索引上的多个节点可以用这个单向链表连接起来,这就解决了键冲突的问题。

例子

  • 举个例子,假设程序要将键值对K2和V2添加到图中的哈希表中,
    并且计算得出K2的索引值为2,那么K1和K2将产生冲突,而解决
    冲突的办法就是使用next指针将键K2和K1所在的节点连接起来
    在这里插入图片描述
  • 因为dictEntry节点组成的链表没有指向链表表尾的指针,所以为了
    速度考虑,程序总是将新节点添加到链表的表头位置(复杂度为O(1)),
    排在其他已有节点的前面
    在这里插入图片描述

rehash

随着操作的不断执行,哈希表保存的键值对会逐渐地增多或者减少,为了让哈希表的负载因子(load factor)维持在一个合理的范围之内,当哈希表保存的键值对数量太多或者太少时,程序需要对哈希表的大小进行相应的扩展或者收缩。扩展和收缩哈希表的工作可以通过执行rehash(重新散列)操作来完成,Redis对字典的哈希表执行rehash的步骤如下:

  • 1.为字典的ht[1]哈希表分配空间,这个哈希表的空间大小取决于要执行的操作,以及ht[0]当前包含的键值对数量(也即是ht[0].used属性的值):
  • 1.1 如果执行的是扩展操纵,那么ht[1]的大小为第一个大于等于ht[0].used * 2的2 ^ n(2的n次幂)
  • 1.2 如果执行的收缩操作,那么ht[1]的大小为第一个大于等于ht[0].used的2^n
  • 2.将保存在ht[0]中的所有键值对rehash到ht[1]上面:rehash指的是重新计算键的哈希值和索引值,然后将键值对放置到ht[1]哈希表的指定位置上。
  • 3.当ht[0]包含的所有键值对都迁移到了ht[1]之后(ht[0]变为空表),释放ht[0],将ht[1]设置为ht[0],并在ht[1]新创建一个空白哈希表,为下一次rehash做准备。

例子

  • 举个例子,假设程序要对图中字典的ht[0]进行扩展操作,
    程序将执行如下步骤
    在这里插入图片描述
    1.ht[0].used当前的值为4,4 * 2 = 8,而8(2^3)恰好是第一个大于等于4的2的n次方,所以程序会将ht[1]哈希表的大小设置为8.
    在这里插入图片描述
    2.将ht[0]包含的四个键值对都rehas到ht[1],如图所示
    在这里插入图片描述
    3.释放ht[0],并将ht[1]设置为ht[0],然后为ht[1]分配一个空白哈希表,如图所示。至此,对哈希表的扩展操作执行完毕,程序成功将哈希表的大小从原来的4改为了现在的8
    在这里插入图片描述

哈希表的扩展与收缩

当以下条件中的任意一个被满足时,程序会自动开始对哈希表执行扩展操作:

  • 1.服务器目前没有在执行BGSAVE命令或者BGREWRITEAOF命令,并且哈希表的负载因子大于等于1
  • 2.服务器目前正在执行BGSAVE命令或者BGREWRITEAOF命令,并且哈希表的负载因子大于等于5
    其中哈希表的负载因子可以通过公式:
    #负载因子 = 哈希表已保存节点数量 / 哈希表大小
    load_factor = ht[0].used / ht[0].size

根据BGSAVE命令或BGREWRITEAOF命令是否正在执行,服务器执行扩展操作所需的负载因子并不相同,这是因为在执行BGSAVE命令或BGREWRITEAOF命令的过程中,Redis需要创建当前服务器进程
的子进程,而大多数操作系统都采用写时复制(copy-on-write)技术来优化子进程的使用效率,所以在子进程存在期间,服务器会提高执行扩展操作所需的负载因子,从而尽可能地避免在子进程存在期间进行哈希表扩展操作,这可以避免不必要地内存写入操作,最大限度地节约内存。
另一方面,当哈希表地负载因子小于0.1时,程序自动开始对哈希表执行收缩操作

例子

  • 例如,对于一个大小为4,包含4个键值对的哈希表来说,这个哈希表的负载因子为load_factor = 4 / 4 = 1;
  • 例如,对于一个大小为512,包含256个键值对的哈希表来说,这个哈希表的负载因子是:load_factor = 256 / 512 = 0.5

渐进式rehash

扩展或收缩哈希表需要将ht[0]里面的所有键值对rehash到ht[1]里面,但是,这个rehash动作并不是一次性、集中式地完成,而是分多次、渐进式地完成的。这样做的原因在于,如果ht[0]里只保存着四个键值对,那么
服务器可以在瞬间就将这些键值对全部rehash到ht[1];但是,如果哈希表里保存的键值对数量不是四个,而是四百万、四千万甚至四亿个键值对,那么要一次性将这些键值对全部rehash到ht[1]的话,庞大的计算量
可能会导致服务器在一段时间内停止服务。因此为了避免rehash对服务器性能造成影响,服务器不是一次性将ht[0]里面地所有键值对全部rehash到ht[1],而是分多次、渐进式地将ht[0]里面地键值对慢慢地rehash到ht[1]

哈希表渐进式rehash的详细步骤:

  • 1.为ht[1]分配空间,让字典同时持有ht[0]和ht[1]两个哈希表

  • 2.在字典中维持一个索引计数器变量rehashidx,并将它的值设置为0,表示rehash工作正式开始

  • 3.在rehash进行期间,每次对字典执行添加、删除、查找或者更新操作时,程序除了执行指定的操作以外,还会顺带将ht[0]哈希表在rehashidx索引上的所有键值对rehash到ht[1],当rehash工作完成之后,程序将
    rehashidx属性的值增一

  • 4.随着字典操作的不断执行,最终在某个时间点上,ht[0]的所有键值对都会被rehash至ht[1],这时程序将rehashidx属性的至设为-1,表示rehash操作已完成。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

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

  • 渐进式rehash执行期间的哈希表操作因为在进行渐进式rehash的过程中,字典会同时使用ht[0]和ht[1]两个哈希表,所以在渐进式rehash进行期间,字典的删除、查找、更新等操作会在两个哈希表上进行。例如,要在字典里面查找一个键的话,程序会在ht[0]里面进行查找,如果没有找到的话,就会继续到ht[1]里面进行查找,诸如此类。另外,在渐进式rehash执行操作期间,新添加到的字典的键值对一律会被保存到ht[1]里面,而ht[0]则不再进行任何添加操作,这一措施保证了ht[0]包含的键值对数量会只减不增,并随着rehash操作的执行而最终变成空表

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

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

相关文章

数据库原理实验课(1)

目录 实验内容 安装头歌中的相关内容 具体过程 完结撒花~ 我也是第一次接触oracle的相关软件和操作,所以是一次傻瓜式教学记录 实验内容 安装头歌中的相关内容 具体过程 这是我在百度网盘中下载解压出来的oracle文件夹内的全部内容(可能有因为安装完…

CodeSys通过C函数接口调用Qt

建议先查看之前的文章【CodeSys中调用C语言写的动态库】,了解如何创建一个能够被codesys调用的动态库。 假如想要在函数中使用Qt或者第三方库(比如opencv等),可以在其自动生成的makefile文件中设置好相应的参数。 比如我这里就是…

2024037期传足14场胜负前瞻

2024037期售止时间为3月9日(周六)20点00分,敬请留意: 本期深盘多,1.5以下赔率4场,1.5-2.0赔率5场,其他场次是平半盘、平盘。本期14场整体难度中等。以下为基础盘前瞻,大家可根据自身…

力扣刷题Days13-101对称二叉树(js)

目录 1,题目 2,代码 2.1递归思想 2.2队列--迭代思想 3,学习与总结 1,题目 给你一个二叉树的根节点 root , 检查它是否轴对称。 2,代码 2.1递归思想 return dfs(left.left, right.right) && dfs(left.right, right.l…

IDS入侵检测知识整理及lDS入侵检查表

【IDS入侵检测知识整理及lDS入侵检查表】 1. 网络入侵检测IDS概念 2. IDS分类 3. IDS的工作原理 4. IDS在网络中的部署 5. 入侵检查表 项目建设全套资料获取通道:软件开发全套资料_数字中台建设指南-CSDN博客

echarts中toolbox 中文乱码问题

问题描述 本地引用的echarts源文件,页面其他部分编码显示正常,唯独toolbox鼠标悬停在上面时提示信息显示乱码。 如图所示: 尝试过的方法 使用sublime text 3,notepad,记事本更改文件编码为utf-8引入时,在sc…

办公电脑换成MacBookPro半年之后……

小白是从2008年开始接触电脑的,当时朋友给我注册的第一个QQ账号是2008年4月。 从此,小白一直认为电脑全部都是Windows系统。直到上大学那年,看到了外教老师的MacBookPro…… 折腾电脑的开始居然是起源于诺基亚手机,给半智能S40的…

程序员有哪些证书值得考?

证书可以作为第三方机构对于程序员特定技能或知识掌握程度的认可,在求职市场上,一些公司尤其是大型企事业单位或者政府项目可能更看重证书作为衡量应聘者专业能力的标准之一。备考证书的过程,也可以促使程序员系统地学习和巩固相关知识&#…

力扣hot100:438.找到字符串中所有字母异位词(滑动窗口)

26个字符,我复制怎么了?26个字符我比较个数怎么了? 顶多时间复杂度*26 本题用固定窗口大小的滑动窗口每次比较包含26个元素的数组次数,最容易写。 动态窗口大小哈希表存数值(双指针差值)难想难写。 一、动态…

基于模拟退火算法(SA)的TSP(Python实现)

本篇文章是博主在最化优学习、人工智能等领域学习时,用于个人学习、研究或者欣赏使用,并基于博主对相关等领域的一些理解而记录的学习摘录和笔记,若有不当和侵权之处,指出后将会立即改正,还望谅解。文章分类在最优化算…

希腊证券交易所ATHEX计划在Sui上部署融资功能

希腊证券交易所ATHEX计划将其电子订单簿构建(Electronic Book Building ,EBB)功能部署到Sui上,他们是第一个与区块链公司合作开发此技术的证券交易所。其背后的雅典交易所集团Athens Exchange Group将与Sui的开发公司Mysten Labs合…

一键转发朋友圈!微信快速营销推广必备法宝!

在这个“得私域者得天下”的互联网时代,如何能够在微信上进行快速、高效的营销推广成为了摆在许多人面前的一道难题。 幸运的是,随着微信管理系统的出现,一键转发朋友圈的快速营销推广法宝已经变得触手可及。 首先,微信管理系统…

QT 解决在线安装版本下载速度(以国内镜像启动安装器的方式)

关于Fiddler输入steam的教程,本人试过,无效略过… 正确方式 具体操作方法: 1.方式一、未安装过QT 下载好安装器之后,在安装器(qt-unified-windows-x64-4.6.0-online.exe)的文件夹中空白处,按…

UVA378 Intersecting Lines 题解

UVA378 Intersecting Lines 题解 怎么这么多点斜式邪教啊。 解法 在计算几何中,我们应该尽可能地避免使用浮点数的计算,尽可能地使用向量计算。 本篇题解默认读者具有向量基础。 为了方便讲解,我们将输入的四个点分别记作 A , B , C , …

20240307-1-前端开发校招面试问题整理JavaScript

前端开发校招面试问题整理【1】——JavaScript 1、JavaScript 基础 Q:介绍 js 的基本数据类型? 基本类型(值类型):String,Number,Boolean,Null,Undefined,S…

WinSCP下载安装并结合内网穿透实现固定公网TCP地址访问本地服务器

文章目录 1. 简介2. 软件下载安装:3. SSH链接服务器4. WinSCP使用公网TCP地址链接本地服务器5. WinSCP使用固定公网TCP地址访问服务器 1. 简介 ​ Winscp是一个支持SSH(Secure SHell)的可视化SCP(Secure Copy)文件传输软件,它的主要功能是在本地与远程计…

FFmpeg教程(干货快速上手)

什么是FFmpeg? FFmpeg是一款免费、开源的跨平台多媒体处理工具,它支持几乎所有的视频格式和编码标准。FFmpeg包含了一系列的子项目和工具,如ffmpeg命令行工具用于转码和处理视频和音频文件,ffplay用于播放多媒体内容,…

穿越牛熊,股市的春天还有多远?

2023年,资本市场的严冬令无数投资者和机构投资者都感受到了前所未有的压力。VC/PE、公募基金、股权投资类公司等机构,在这一年里业绩普遍不佳,寒意弥漫。VC/PE机构的营业收入普遍呈现负增长,公募基金更是历史上首次连续两年亏损&a…

LeetCode 刷题 [C++] 第3题.无重复字符的最长子串

题目描述 给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。 题目分析 可以使用滑动窗口加哈希表来实现: 使用start和end两个变脸来表示滑动窗口的头部位置和尾部位置,两者开始均为0;借助哈希表来记录已经遍…

Unity 给刚体一个力或速度

创建平面和小球,给力或给速度让其弹起 给小球挂载刚体(Rigibdody)和脚本 (力是累计或者衰减的,直接给速度就是赋值,但如果速度就和力类似了) using System.Collections; using System.Collect…