基础提升*哈希函数与哈希表

目录

1:哈希函数与哈希表

2:利用哈希表建立新的结构:

3:布隆过滤器(黑名单的查询与建立)

4:一致性哈希

情景假设:

原始问题:

 解决方法(一致性哈希)

 一致性哈希的优点:

存在的问题:

解决方法:

虚拟节点在一致性哈希中的变化使用:


1:哈希函数与哈希表

        输入域无穷,输出域有穷尽

        相同的输入参数返回相同的输出(内部不含随机)

        不同的输入可能会导致相同的输出(哈希碰撞)

        将输入对应的输出均匀离散到输出域上

使用样例:查询出现次数最多的数字,传统方法建立哈希表,但是如果考虑每个数字都会出现的最坏情况会使得哈希表的占用内存巨大。

改进方法:将待处理数字经过哈希函数得到的结果进行模m处理,根据哈希函数的特性,模m后的结果也近似均匀的分布于(0,m-1)范围上,根据处理后的结果建立元素个数为m的哈希表,再对每个哈希表进行处理

2:利用哈希表建立新的结构:

 建立两个hashmap,一个是key-size;另一个是size-key

加入比较简单每一个可以假如是,对应的size也要写入++后的结果

在等概率随机返回时,在map2中选择0-sizemax中任意的数做索引进行返回。

在删除时,为了避免直接删除产生漏洞,导致删除后的getRandom出现错误,需要建立最后一个的key和被删除的size的对应关系(用最后一个的key覆盖被删除的key的位置),并在两个map中更新,将原最后一对删除。这样代表最后进入的sizemax建议方便进行下一次删除。

 

3:布隆过滤器(黑名单的查询与建立)

可能会误杀但是不会错过,

在建立黑名单时,首先建立一个m大小bit的位图空间,

将待处理url通过k个哈希函数,将得到的k个哈希值进行模m计算,然后将对应位置bit“描黑”,

在对一个待判断的url进行判断是判断得到的k个哈希值模计算后的格子是否都为黑,如果有不为黑的那么就不存于黑名单中。

所以通过这种方式不会错过但有可能误杀。

需要调整的参数时k和m

m越大失误率越小。

但k的大小应该根据m的大小和样本数量改变,而不是单纯的越大或者越小就更好。(因为k多表示选取的特征多失误率会变小,但是k越大,也会造成mbit空间的耗尽。)

设计布隆过滤器时需要的已知参数:

n:样本量   p:失误率

计算公式:

1,2计算出理论值(2中的k用理论值算),根据实际情况可能给出可以允许使用的内存(比如算出m要26G,但是面试官说可以30G),那么将2得到的k和实际允许的m带到3中计算出实际可以得到的真实失误率。

1:确定需要多少位m:

计算向上取整

(这里计算的m的单位是bit,转换为字节数还要除以8)

 2:确定需要多少个哈希函数k:

计算向上取整

 3:如果给定了允许的占用内存数(m):

4:一致性哈希

转载于:白话解析:一致性哈希算法 consistent hashing-朱双印博客 (zsythink.net)

情景假设:

假设,我们有三台缓存服务器,用于缓存图片,我们为这三台缓存服务器编号为0号、1号、2号,现在,有3万张图片需要缓存,我们希望这些图片被均匀的缓存到这3台服务器上,以便它们能够分摊缓存的压力。也就是说,我们希望每台服务器能够缓存1万张左右的图片,那么,我们应该怎样做呢?如果我们没有任何规律的将3万张图片平均的缓存在3台服务器上,可以满足我们的要求吗?可以!但是如果这样做,当我们需要访问某个缓存项时,则需要遍历3台缓存服务器,从3万个缓存项中找到我们需要访问的缓存,遍历的过程效率太低,时间太长,当我们找到需要访问的缓存项时,时长可能是不能被接受的,也就失去了缓存的意义,缓存的目的就是提高速度,改善用户体验,减轻后端服务器压力,如果每次访问一个缓存项都需要遍历所有缓存服务器的所有缓存项,想想就觉得很累,那么,我们该怎么办呢?原始的做法是对缓存项的键进行哈希,将hash后的结果对缓存服务器的数量进行取模操作,通过取模后的结果,决定缓存项将会缓存在哪一台服务器上,这样说可能不太容易理解,我们举例说明,仍然以刚才描述的场景为例,假设我们使用图片名称作为访问图片的key,假设图片名称是不重复的,那么,我们可以使用如下公式,计算出图片应该存放在哪台服务器上。

hash(图片名称)% N

因为图片的名称是不重复的,所以,当我们对同一个图片名称做相同的哈希计算时,得出的结果应该是不变的,如果我们有3台服务器,使用哈希后的结果对3求余,那么余数一定是0、1或者2,没错,正好与我们之前的服务器编号相同,如果求余的结果为0, 我们就把当前图片名称对应的图片缓存在0号服务器上,如果余数为1,就把当前图片名对应的图片缓存在1号服务器上,如果余数为2,同理,那么,当我们访问任意一个图片的时候,只要再次对图片名称进行上述运算,即可得出对应的图片应该存放在哪一台缓存服务器上,我们只要在这一台服务器上查找图片即可,如果图片在对应的服务器上不存在,则证明对应的图片没有被缓存,也不用再去遍历其他缓存服务器了,通过这样的方法,即可将3万张图片随机的分布到3台缓存服务器上了,而且下次访问某张图片时,直接能够判断出该图片应该存在于哪台缓存服务器上,这样就能满足我们的需求了,我们暂时称上述算法为HASH算法或者取模算法,取模算法的过程可以用下图表示。

hash.png

但是,使用上述HASH算法进行缓存时,会出现一些缺陷,试想一下,如果3台缓存服务器已经不能满足我们的缓存需求,那么我们应该怎么做呢?没错,很简单,多增加两台缓存服务器不就行了,假设,我们增加了一台缓存服务器,那么缓存服务器的数量就由3台变成了4台,此时,如果仍然使用上述方法对同一张图片进行缓存,那么这张图片所在的服务器编号必定与原来3台服务器时所在的服务器编号不同,因为除数由3变为了4,被除数不变的情况下,余数肯定不同,这种情况带来的结果就是当服务器数量变动时,所有缓存的位置都要发生改变,换句话说,当服务器数量发生改变时,所有缓存在一定时间内是失效的,当应用无法从缓存中获取数据时,则会向后端服务器请求数据,同理,假设3台缓存中突然有一台缓存服务器出现了故障,无法进行缓存,那么我们则需要将故障机器移除,但是如果移除了一台缓存服务器,那么缓存服务器数量从3台变为2台,如果想要访问一张图片,这张图片的缓存位置必定会发生改变,以前缓存的图片也会失去缓存的作用与意义,由于大量缓存在同一时间失效,造成了缓存的雪崩,此时前端缓存已经无法起到承担部分压力的作用,后端服务器将会承受巨大的压力,整个系统很有可能被压垮,所以,我们应该想办法不让这种情况发生,但是由于上述HASH算法本身的缘故,使用取模法进行缓存时,这种情况是无法避免的,为了解决这些问题,一致性哈希算法诞生了。

原始问题:

问题1:当缓存服务器数量发生变化时,会引起缓存的雪崩,可能会引起整体系统压力过大而崩溃(大量缓存同一时间失效)。

问题2:当缓存服务器数量发生变化时,几乎所有缓存的位置都会发生改变,怎样才能尽量减少受影响的缓存呢?

 解决方法(一致性哈希)

一致性哈希算法是对2^32取模

圆环的正上方的点代表0,0点右侧的第一个点代表1,以此类推,2、3、4、5、6……直到2^32-1,也就是说0点左侧的第一个点代表2^32-1

我们把这个由2的32次方个点组成的圆环称为hash环。

三台服务器肯定有自己的IP地址,我们使用它们各自的IP地址进行哈希计算,使用哈希后的结果对2^32取模

hash(服务器A的IP地址) %  2^32

hash(服务器B的IP地址) %  2^32

hash(服务器C的IP地址) %  2^32

我们仍然使用图片的名称作为找到图片的key,那么我们使用如下公式可以将图片映射到上图中的hash环上。

hash(图片名称) %  2^32

判断一个对象应该被缓存到哪台服务器上的,将缓存服务器与被缓存对象都映射到hash环上以后,从被缓存对象的位置出发,沿顺时针方向遇到的第一个服务器,就是当前对象将要缓存于的服务器,由于被缓存对象与服务器hash后的值是固定的,所以,在服务器不变的情况下,一张图片必定会被缓存到固定的服务器上,那么,当下次想要访问这张图片时,只要再次使用相同的算法进行计算,即可算出这个图片被缓存在哪个服务器上,直接去对应的服务器查找对应的图片即可。

 一致性哈希的优点:

假设,服务器B出现了故障,我们现在需要将服务器B移除,那么,我们将上图中的服务器B从hash环上移除即可,移除服务器B以后示意图如下。

在服务器B未移除时,图片3应该被缓存到服务器B中,可是当服务器B移除以后,按照之前描述的一致性哈希算法的规则,图片3应该被缓存到服务器C中,因为从图片3的位置出发,沿顺时针方向遇到的第一个缓存服务器节点就是服务器C,也就是说,如果服务器B出现故障被移除时,图片3的缓存位置会发生改变

但是,图片4仍然会被缓存到服务器C中,图片1与图片2仍然会被缓存到服务器A中,这与服务器B移除之前并没有任何区别,这就是一致性哈希算法的优点,如果使用之前的hash算法,服务器数量发生改变时,所有服务器的所有缓存在同一时间失效了,而使用一致性哈希算法时,服务器的数量如果发生改变,并不是所有缓存都会失效,而是只有部分缓存会失效,前端的缓存仍然能分担整个系统的压力,而不至于所有压力都在同一时间集中到后端服务器上。

存在的问题:

服务器分布不均匀,造成服务器忙闲负载不同。

解决方法:

虚拟节点技术

如果想要均衡的将缓存分布到3台服务器上,最好能让这3台服务器尽量多的、均匀的出现在hash环上,但是,真实的服务器资源只有3台,我们怎样凭空的让它们多起来呢,没错,就是凭空的让服务器节点多起来,既然没有多余的真正的物理服务器节点,我们就只能将现有的物理节点通过虚拟的方法复制出来,这些由实际节点虚拟复制而来的节点被称为”虚拟节点”。加入虚拟节点以后的hash环如下。

“虚拟节点”是”实际节点”(实际的物理服务器)在hash环上的复制品,一个实际节点可以对应多个虚拟节点。

从上图可以看出,A、B、C三台服务器分别虚拟出了一个虚拟节点,当然,如果你需要,也可以虚拟出更多的虚拟节点。引入虚拟节点的概念后,缓存的分布就均衡多了,上图中,1号、3号图片被缓存在服务器A中,5号、4号图片被缓存在服务器B中,6号、2号图片被缓存在服务器C中,如果你还不放心,可以虚拟出更多的虚拟节点,以便减小hash环偏斜所带来的影响,虚拟节点越多,hash环上的节点就越多,缓存被均匀分布的概率就越大。

虚拟节点在一致性哈希中的变化使用:

还可以根据服务器的能力不同,改变不同服务器对应的虚拟节点的数量,比如某个服务器能力超强,那么让这台服务器对应的虚拟节点的数量变多,这样会使这个服务器“能者多劳”。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值