哈希算法原来有这么多应用场景!

  • 不存在

该图片不在图库

  • 存在

再通过hash表中存储的文件路径,获取该已存在图片,跟现在要插入的图片做全量比对,看是否完全一样:

  • 一样

说明已经存在

  • 不一样

说明两张图片尽管唯一标识相同,但并非相同图片

3.3 数据校验


P2P下载,从多个机器并行下载一个2G电影,该电影文件可能被分割成很多文件块。等所有的文件块都下载完成之后,再组装成一个完整的电影文件就行了。

网络传输是不安全的,下载的文件块有可能是被宿主机器恶意修改过的,又或者下载过程中出现了错误,所以下载的文件块可能不是完整的。如果没有能力检测这种恶意修改或者文件下载出错,就会导致最终合并后的电影无法观看,甚至导致电脑中毒。

如何校验文件块的安全、正确、完整?

使用哈希算法对100个文件块分别取哈希值,并保存在种子文件。

只要文件块内容有丁点改变,最后哈希值就完全不同。

所以,当文件块下载完成后:

  • 使用相同哈希算法对下载好的文件块逐一求哈希

  • 对比种子文件中的哈希值:

  • 若不同

说明该文件块不完整或被篡改,重新从其它宿主机器下载该文件块

3.4 Hash函数


该场景:

  • 对hash算法冲突的要求较低,偶尔hash冲突问题不大

  • 也不关心hash函数对于hash算法计算得到的值,是否能反向解密

hash函数中用到的hash算法,更加关注hash后的值是否能均匀分布。

hash函数执行的快慢,也影响hash表的性能,所以,hash函数用的hash算法一般较简单,追求效率。

哈希算法还能解决很多分布式问题。

3.5 负载均衡


实现一个会话粘滞(session sticky)负载均衡算法,需在同一客户端上,在一次会话中的所有请求都路由同一服务器。

最直接的维护一张映射关系表:

客户端IP地址或会话ID =》服务器编号的映射关系

客户端发出的每次请求,先映射表查找路由的服务器编号,再请求对应服务器。

简单直观,但:

  • 客户端很多

映射表可能会很大,比较浪费内存空间

  • 客户端下线、上线,服务器扩容、缩容都会导致映射失效

维护映射表的成本很大

借助哈希算法即可解决:对客户端IP地址或会话ID计算哈希值,与服务器列表的大小取模,最终得到的值即被路由到的服务器编号。

这就可以把同一IP过来的所有请求,都路由到同一后端服务器。

3.6 数据分片


统计关键词

假如1T日志文件,这里面记录了用户的搜索关键词,快速统计出每个关键词被搜索的次数。

这个问题有两个难点:

  • 搜索日志很大,没法放到一台机器的内存

  • 如果只用一台机器来处理这么巨大的数据,处理时间会很长。

可以先对数据进行分片,然后采用多台机器处理提高处理速度:

用n台机器并行处理:

  • 从搜索记录的日志文件依次读出每个搜索关键词

  • 通过哈希函数计算哈希值

  • 再跟n取模

  • 得到应该被分配到的机器编号

哈希值相同的搜索关键词就被分配到了同一个机器上。即同一个搜索关键词会被分配到同一个机器上。每个机器会分别计算关键词出现的次数,最后合并起来就是最终的结果。

MapReduce的基本思想。

快速判断图片是否在图库

前面提到可以给每个图片取唯一标识(或者信息摘要),然后构建hash表。

现在图库现有1亿张图片,单机构建hash表就做不到了。因为单台机器的内存有限,而1亿张图片构建散列表显然远远超过了单台机器的内存上限。

我们同样可以对数据进行分片,然后采用多机处理。我们准备n台机器,让每台机器只维护某一部分图片对应的散列表。我们每次从图库中读取一个图片,计算唯一标识,然后与机器个数n求余取模,得到的值就对应要分配的机器编号,然后将这个图片的唯一标识和图片路径发往对应的机器构建散列表。

当我们要判断一个图片是否在图库中的时候,我们通过同样的哈希算法,计算这个图片的唯一标识,然后与机器个数n求余取模。假设得到的值是k,那就去编号k的机器构建的散列表中查找。

现在,我们来估算一下,给这1亿张图片构建散列表大约需要多少台机器。

散列表中每个数据单元包含两个信息,哈希值和图片文件的路径。假设我们通过MD5来计算哈希值,那长度就是128比特,也就是16字节。文件路径长度的上限是256字节,我们可以假设平均长度是128字节。如果我们用链表法来解决冲突,那还需要存储指针,指针只占用8字节。所以,散列表中每个数据单元就占用152字节(这里只是估算,并不准确)。

假设一台机器的内存大小为2GB,散列表的装载因子为0.75,那一台机器可以给大约1000万(2GB*0.75/152)张图片构建散列表。所以,如果要对1亿张图片构建索引,需要大约十几台机器。在工程中,这种估算还是很重要的,能让我们事先对需要投入的资源、资金有个大概的了解,能更好地评估解决方案的可行性。

海量数据处理都可采用多机分布式处理方案。分片设计以突破单机内存、CPU等资源的限制。

3.7 分布式存储


为提高数据读、写能力,一般采用分布式存储,比如分布式缓存。因为大量数据要缓存,所以单机缓存肯定不够,就需要将数据分布在多机。

哪个数据又该存在哪个机器呢?

参考数据分片设计,通过哈希算法处理数据,然后对机器个数取模,就得到该缓存数据应存储的机器编号。

但若数据增多,原先10个机器已无法支撑,就得扩容,比如扩到11个节点。

但问题也来了,原先数据通过与10取模,现在节点多了一个,所有数据需重新计算哈希值,然后迁移到现在的对应节点。

这时,原缓存中的数据突然大量失效,这些缓存数据的请求就会穿透缓存,直接请求DB。

所以,此时我们就需要一个方案,能在加新节点后,无需做大量数据迁移。

救星就是一致性 hash 算法!

先假设:

  • 有k个节点

  • 数据哈希值范围[0, MAX]

  • 将整个范围划分成m个小区间(m>>k)

  • 每个节点负责m/k个小区间

加入新节点时,就将某几个小区间的数据,从原节点迁移至新节点。

这就避免了全部重新哈希、搬移数据,也保持了各个机器上数据数量的均衡。

这就是一致性哈希算法的基本思想。

案例

钟表有 60 分钟,从 0 开始到 59,共 60 个点。

现在将机器往这 60 个点分配,规则如下:

hash(ip) % 60

假设 3 台机器 A,B 和 C,分别被分配到了 14,37 和 46 三个点。

图片的分配规则类似:

hash(image_id) % 60

现有 3 张图片 x, y, z,分别被分配到 5,30,50 这三个点。

图片都没被分配到机器的节点,这咋办?

在钟表上顺时钟往前寻找,第一台遇到的机器,就是它的归属。

不凑巧,A B C 三台机器分别分配到 5,10,15 这三个点。这样对 A 很不公平啊!要负责存储绝大多数的图片,那这怎么办呢?

为避免不必要争端,于是引入“虚拟节点”,每台机器都可拔一根汗毛,变成若干台,把虚拟节点分散到 60 个点,归属“虚拟节点”的图片,均保存到它的真身。这就能解决分配不均匀问题。

应用时,将 60 替换下即可,如替换为 2^32。

参考

  • https://en.wikipedia.org/wiki/Consistent_hashing
  • https://www.zsythink.net/archives/1182
  • 24
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值