本文指的是源代码版本是
词典,也称为“传播”列表,是用于存储键值对的数据结构。它以许多高级语言(例如PHP数组)实现。但是,C语言没有此数据结构。REDIS是K-V数据库。整个数据库由字典存储。实际添加,删除,删除,删除,删除,删除,删除和删除dittionary.Change和检查操作。
根据REDIS数据库的特征,字典具有以下特征。
,英语称为“哈希表”,我们通常称其为“哈希表”或“哈希表”,
分布列表使用数组支持遵循随机访问数据的特征,因此,差异列表实际上是阵列的扩展,从数组演变出来。可以说,如果没有数组,则不会散布列表。为了将我们的钥匙映射到哈希表,中间是通过耗散计算的,并且可以尽可能地对其进行操纵。
lollow函数:
顾名思义,LOLTIC函数是一个函数。我们可以将其定义为具有(键),其中键代表元素的密钥值,而哈希(key)的值表示由散射功能。
在上图中,映射到0的散布列表的哈希(键)是我们的悠久函数。
Lollow冲突:
尽管分布函数将尽可能均匀地分布在哈希表中,但对于同一哈希表的向下投标仍然存在多个不同的键。这是解决以下内容的主要方法:
1)开放式寻址方法:
核心思想:如果存在弱势冲突,请重新检测一个自由立场并插入它。
经典检测算法有三种类型;在这里,我们以线性检测为例,简要讨论其原理。
线性检测的原理是,当我们将数据插入分布列表中时,如果某个数据已被存储函数的扩展占据,我们将从当前位置开始,以后查看是否存在任何存在。哈希冲突,数据映射到3或4.当然,这是一个理想的情况,您可以在操作一步之后找到正确的位置。当哈希表元素很多且加载时,在许多步骤后可能无法找到正确的位置。目前,性能将急剧下降。
适用的开放执法方案:当数据量相对较小并且加载因子很小时,开放的寻址方法是合适的。这就是为什么Java中的ThreadLocalMap使用开放的寻址方法来解决大冲突。
2)链接:
与开放式寻址方法的实践相比,链接列表方法直接放弃了此方法,但在映射的地图位置进行了处理;这样,多个元素可能会出现在相同的哈希插槽中,并且可能会出现多个元素。这次在串联使用。
适用的方案:基于链接的大型冲突处理方法更适合于存储大型物体和大量分散列表的数据量,它比开放式寻址方法更灵活,并支持更多的优化策略,例如更换红树和黑树而不是链接。
如何设计延迟?
在极端情况下,所有数据均在铺设功能之后分散到同一插槽中。如果我们使用基于链接的列表的冲突解决方案,那么此时,分散列表将使查询的时间复杂性从O中急剧降低。(1)到O(n)。
如果分配列表中有100,000个数据,则降解后分散列表查询的效率将减少100,000次。直接将其放置,如果仅需0.1秒才能在100次之前运行100次,现在需要10,000秒,现在需要10,000秒。方式,由于查询操作运行,因此可以消耗大量的CPU或线程资源,因此系统无法响应其他请求,从而实现了拒绝服务攻击的目的(DOS)。这是分散的基本原理列出碰撞攻击。
在延迟的设计中应考虑两个原则:
如果负载因子太大,该怎么办?
Essencenot仅应插入数据多次解决或长链的过程,搜索过程将变得非常慢。
对于阵列的扩展,数据运动操作相对简单。但是,对于扩展点列表的扩展,数据移动操作要复杂得多。由于散射列表的大小已更改,并且数据的存储位置已更改也发生了变化,我们需要通过分布函数重新计算每个数据的存储位置。
插入数据,最好将其扩展。最好的时间复杂性是o(1)。在最坏的情况下,加载因子太高而无法启动容量。我们需要重新应用记忆空间,计算哈希位置并移动数据,因此时间复杂性为O(n)。失速还分析了该方法。当摊位都是摊位时,时间复杂性就接近最佳情况,即o(1)。
负载因子阈值的设置应权衡时间和空间的复杂性。如果记忆空间不紧,并且执行效率要求很高,则可以减少负载因子的阈值;相反,如果记忆空间很紧,并且执行效率不高,则可以增加负载因子的值,甚至大于1。
如何避免效率低下的扩张?
在大多数情况下,动态扩展列表会快速插入数据,但是在特殊情况下,当加载因子达到阈值时,它需要首先。这次,插入数据将变得缓慢甚至不可接受。
如果我们的业务代码直接为用户服务,尽管大多数情况都可以迅速运行,但它也会导致用户插入非常缓慢的插入操作崩溃。这次,“一个时间”扩展机制不合适。
为了解决过多的一个时间容量扩展的情况,我们可以将容量扩展操作纳入插入操作的过程中。当加载因子达到阈值时,我们只申请新空间,但我们不移动旧数据到新的分散列表。
插入新数据后,我们将新数据插入新的分散列表中,并从旧散落列表中取出数据以将其放入新的分散列表中。每次我们将数据插入到分布列表中,我们都会重复上述数据流程。多次插入操作后,旧散落列表中的数据将不一致地移至新的分散列表。这样,就没有集中的一次性数据移动,并且插入操作已经快速。
如何在此期间进行查询操作?对于查询操作,为了兼容新的和旧的时尚列表中的数据。
通过。在任何情况下,插入数据的时间复杂性是O(1)。
在第一部分中,我们介绍了词典的基本原理,以及在解决实际问题时需要解决的几个关键问题。因此,让我们看一下Redis如何结合自己的场景来解决这些主要问题。
此图片仍然是Redis的字典本质上是该模型,即链接列表方法。
我相信您还了解到,这里的哈希算法对应于上面的哈希(键),并且需要解决与哈希表的映射关系。
当使用字典用作数据库的基础实现,或实现哈希键的底层时,Redis使用Hurmurhash2算法来计算键的哈希值。
Murmurhash算法最初是由Austin Appleby在2008年发明的。
Murmurhash算法的最新版本是Murmurhash3,而Redis使用Murmurhash2。有关Murmurhash算法的更多信息,请参阅算法的首页:http://code.google.com/smhashr/。
在上面,我们引入了大规模冲突,以及两类解决方案及其应用程序方案。我相信您也很清楚。对于大量数据,例如REDIS,您可以使用我们提到的解决方案。
没错,Redis使用解决方案来解决冲突。
Redis的哈希表使用截面链条方法来解决关键冲突。每个哈希表节点都有下一个指针,并且可以使用多个哈希表节点与下一个指针形成一个链接列表。索引上的多数节点可以与此一条路链接列表连接,从而解决了解决问题的问题关键冲突。
在Layman的演讲中,在使用哈希表的过程中,数据变得越来越密集。目前,将考虑扩大维持哈希表效率的能力;反之亦然。
在其中,如果称为哈希表元素的密集性的习惯,则有必要考虑将其保持在合理的范围内。当哈希表保存的键值数量太大或太小时,该程序需要是hash。表的大小相对应。
可以通过执行操作来完成扩展和合同的哈希表的工作。Redis词典的哈希表的步骤如下:
1)词典的HT [1]哈希表分配空间。该哈希表的大小取决于要执行的操作,以及当前包括ht [0]的密钥值数(即ht [0]。
直接查看源代码,更清晰。
2)HT [0]中存储的所有密钥值都在重新升级到HT [1]:重新指代重新计算的密钥的哈希值和索引值,然后将键值放在HT [1] HAHAHAIN中。嬉皮士的位置。
3)当将HT [0]中包含的所有关键值迁移到HT [1](ht [0]中),释放ht [0],将ht [1]设置为ht [0]HT [1]中的一个新的空白哈希桌子为下一个重新进行准备。
哈希表的扩展和收缩
这
根据BGSAVE命令或bgrewriteaof命令正在执行,服务器的扩展操作所需的负载因子不同,因为在执行BGSAVE命令或Bgrewriteao f命令的过程中,REDIS需要创建子 -当前服务器流程的过程和BIG是大量的操作系统使用复印件技术来优化子过程的使用效率。
因此,在子过程的存在期间,服务器将改善执行扩展所需的负载因子,以避免在子过程存在期间的哈希表扩展操作。SAVE内存。
另一方面,当哈希表的负载因子小于0.1时,该程序会自动开始在哈希表上执行收缩操作。
扩展或收缩的哈希表需要将所有关键值放入ht [0]中的所有键值,但这是。
这样做的原因是,如果HT [0]中只有四个键值对,则服务器可以立即将这些键值支付给所有重新对HT [1];值不是四个,而是400万,40,40百万甚至4亿关键值。然后,如果您需要一次,则需要将这些关键价值支付给所有重新升级到HT [1]。
因此,为了避免Rehash对服务器性能的影响,服务器并没有一次将HT [0]中的所有关键值放在HT [1]中,而是在HT [0]中。在HT [0]多次,逐渐地,将HT [0]分为HT [0]。键值缓慢地重新为HT [1]。
以下是哈希表逐渐重新校正的详细步骤:
本质
在进行性重新进行的执行期间,因为在渐进式rehash过程中,字典将同时使用HT [0]和HT [1]。(删除),查找,更新等。将执行操作在两个哈希表上。
例如,如果您想在字典中找到一个键,则该程序将在HT [0]中找到。如果找不到的话,您将继续在HT [1]中找到它,等等。
此外,在实施高级重复时,新添加到字典的关键值将保存在HT [1]中,而HT [0]将不再执行任何其他操作。此措施确保HT [0]键值的量仅包含减少或或不减少,并且随着重新操作的执行,最终变为空。
字典结构被广泛用于Redis。例如,我们经常说Redis是K-V的存储结构。其中,这是使用字典来存储K-V结构。
此外,还有许多其他地方可以使用它。例如,redis,使用字典存储所有主节点和从属节点;
在另一个示例中,当数据库中的键值对值是哈希类型值的字典。在不同的应用程序中,字典中的键值可能不同,而docttype结构是一组操作函数抽象以实现各种形式的字典。
数据结构取决于Redis字典主要包括三个部分:,。两个哈希表被嵌入到字典中。哈希表中的表字段存储哈希仪节点。哈希表节点对应于钥匙值对。总体结构如下:
1)字典
它的主要作用是在分散列表上执行另一层包装。当需要执行字典时,需要执行字典时使用辅助字段。
在不同的应用程序中,字典中的键值可能会有所不同,而Dandtype结构是一组操作函数,以抽象以以各种形式获得字典。
2)哈希表:
哈希表的结构占整体上的32个字节。表字段是一个数组,角色是存储键值对。阵列中的元素指向dictentry结构。每个dictentry中都有一个钥匙值对。尺寸表示表数组的总大小。所使用的字段记录了表数组上的键数。
Sizemask字段用于计算键的索引值,Sizemask的值等于Size-1。
3)哈希表节点:
哈希表中的元素结构与我们的自定义元素结构相似。总体占据24个字节,关键字段存储在钥匙值中。v字段是一个组合,该组合存储了钥匙值的值,并在不同方案中使用了不同的字段。
例如,当将整个Redis数据库的所有键值与字典存储时,该字段用于指向不同类型的值。例如,当字典用作记录键的到期时间时,使用字段存储;
当出现哈希冲突时,下一个字段将用于指向冲突元素,并通过形成单个链接列表。
在Redis服务器的启动期间,整个数据库将初始化一个空词典来存储整个数据库的键值对。启动一个空词典,dict.h文件中的dictCreate函数被调用。相应的源代码是:
DICTCREATE函数初始化一个空词典的主要步骤是:应用空间,调用_dictinit函数,并给出了字典每个字段的初始值。
1)dictadd:
DICTADD函数的主要作用是添加键值对,执行步骤如下。
1)调用dictAddraw函数,添加键,然后返回字典键中的null,否则将键添加到哈希表中并返回到新的哈希节点。
2)将新节点的值设置为返回,即更新其Val字段。
2)dictAddraw:
DICTADDRAW函数的主要角色是添加或查找密钥,将成功的返回添加到新节点,找到成功返回null并将旧节点存储到现有字段中。
此功能的核心是调用该功能。角色是获得密钥的索引值。有两个步骤:
1.数学扩展
随着REDIS数据库添加操作,存储密钥值的字典将无法达到上限的能力不足。目前,词典的哈希表需要扩展。容量的相应源代码是dict.h文件中的函数。
扩展的主要过程是:
当然,它不仅可以扩大空间,而且可以缩小。
2.进行重新进行
除了扩展外,还将触发重新启动,并将在收缩期间触发。Redis的整个重新进行的实现主要分为以下步骤。
Redis优化的想法非常聪明。使用思想分割来执行重新操作,一般步骤如下。
在执行操作,删除,搜索,修改和其他操作之前,请判断当前的字典重新操作是否正在进行中,并且使用DictrehashTep函数进行重新操作(每个节点只能执行一个节点,并且一次执行1次。)本质
除了这些操作外,如果当前的字典还需要执行REHSH操作,则将逐步划分功能进行批处理操作(每次进行100个节点进行重复操作,并且总共执行1毫秒)。
n重新操作后,整个HT [0]数据将迁移到HT [1]。,因此忽略需要时间。
找到密钥的源代码,整个过程相对简单,主要分为以下步骤。
dict.h文件中的dictdelete函数,主要执行过程是:
主要过程:
当字典中的数据通过一系列操作并且总空间小于10%时,将执行收缩操作,并且REDIS数据库在没有废物存储器的情况下占据了合理范围内的内存。
缩小功能:
整个收缩步骤大致是:确定当前容量是否达到最小阈值,即使用/尺寸<10%,当达到分子函数时,称为essesencethe收缩操作,扩展操作相似。最终调用是函数,随后的操作与扩展一致。
从redis的源代码中,我们可以看到有多个位置可以调用该函数并调用该函数执行重复。它们是:dictaDdraw,dictgnericdelete,dictfind,dictsomekeys。
无论添加和删除哪个操作,5DictrehassStep函数称为这5个函数,并且Dictrehash环路的回路数值为1:
这样,每次迁移哈希插槽时,哈希表都会执行正常的添加和删除请求操作。
实施Rehash时,您需要解决什么问题?有三个要点:Rehash触发何时触发?Rehash扩展了多大?如何执行Rehash?与文章结合在一起,您可以自己考虑一下。
为了实现高性能的哈希表不仅是重新的需求,而且是许多计算机系统开发的重要目标。在实现具有出色性能的哈希表中,您需要专注于解决和解决这两个问题。
好的,到目前为止,您已经了解了本文的两个核心:而且,您认为它已经结束了吗?
答案是否定的,厌恶方法引入的逐渐恢复解决了高效率扩展的问题,这也给字典遍历带来了一些困难。关于字典遍历问题,让我们谈谈下一部分?