hash 算法原理及应用漫谈( 三 )


注:对于git里tree、parent等结构感兴趣的同学,可以参考下这篇文章《Git 内部原理 - Git 对象》,这里由于篇幅原因就不进行深入分析了 。

  • 版权校验
  • 在数据校验方面的另一个应用场景就是版权的保护或者违禁信息的打击,比如某个小视频,第一个用户上传的时候,我们认为是版权所有者,计算一个hash值存下来 。当第二个用户上传的时候,同样计算hash值,如果hash值一样的话,就算同一个文件 。这种方案其实也给用户传播违禁文件提高了一些门槛,不是简单的换一个名字或者改一下后缀名就可以躲避掉打击了 。(当然这种方式也是可以绕过的,图片的你随便改一下颜色,视频去掉一帧就又是完全不同的hash值了 。注意:我没有教你变坏,我只是和你在讨论这个技术 。。。)另外我们在社区里,也会遇到玩家重复上传同一张图片或者视频的情况,使用这种校验的方式,可以有效减少cos服务的存储空间 。
  • 大文件分块校验
  • 使用过bt的同学都有经验,在p2p网络中会把一个大文件拆分成很多小的数据各自传输 。这样的好处是如果某个小的数据块在传输过程中损坏了,只要重新下载这个块就好 。为了确保每一个小的数据块都是发布者自己传输的,我们可以对每一个小的数据块都进行一个hash的计算,维护一个hash List,在收到所有数据以后,我们对于这个hash List里的每一块进行遍历比对 。这里有一个优化点是如果文件分块特别多的时候,如果遍历对比就会效率比较低 。可以把所有分块的hash值组合成一个大的字符串,对于这个字符串再做一次Hash运算,得到最终的hash(Root hash) 。在实际的校验中,我们只需要拿到了正确的Root hash,即可校验Hash List,也就可以校验每一个数据块了 。

hash 算法原理及应用漫谈

文章插图
大文件分块示意图
4.3 负载均衡
活动开发同学在应对高星级业务大用户量参与时,都会使用分库分表,针对用户的openid进行hashtime33取模,就可以得到对应的用户分库分表的节点了 。
hash 算法原理及应用漫谈

文章插图
活动分库分表示意图
如上图所示,这里其实是分了10张表,openid计算后的hash值取模10,得到对应的分表,在进行后续处理就好 。对于一般的活动或者系统,我们一般设置10张表或者100张表就好 。
下面我们来看一点复杂的问题,假设我们活动初始分表了10张,运营一段时间以后发现需要10张不够,需要改到100张 。这个时候我们如果直接扩容的话,那么所有的数据都需要重新计算Hash值,大量的数据都需要进行迁移 。如果更新的是缓存的逻辑,则会导致大量缓存失效,发生雪崩效应,导致数据库异常 。造成这种问题的原因是hash算法本身的缘故,只要是取模算法进行处理,则无法避免这种情况 。针对这种问题,我们就需要利用一致性hash进行相应的处理了 。
一致性hash的基本原理是将输入的值hash后,对结果的hash值进行2^32取模,这里和普通的hash取模算法不一样的点是在一致性hash算法里将取模的结果映射到一个环上 。将缓存服务器与被缓存对象都映射到hash环上以后,从被缓存对象的位置出发,沿顺时针方向遇到的第一个服务器,就是当前对象将要缓存于的服务器,由于被缓存对象与服务器hash后的值是固定的,所以,在服务器不变的情况下,一个openid必定会被缓存到固定的服务器上,那么,当下次想要访问这个用户的数据时,只要再次使用相同的算法进行计算,即可算出这个用户的数据被缓存在哪个服务器上,直接去对应的服务器查找对应的数据即可 。这里的逻辑其实和直接取模的是一样的 。如下图所示:
hash 算法原理及应用漫谈

文章插图
初始3台机器的情况
初始情况如下:用户1的数据在服务器A里,用户2、3的数据存在服务器C里,用户4的数据存储在服务器B里
【hash 算法原理及应用漫谈】下面我们来看一下当服务器数量发生变化的时候,相应影响的数据情况:
  • 服务器缩容

hash 算法原理及应用漫谈

文章插图
服务器缩容
服务器B发生了故障,进行剔除后,只有用户4的数据发生了异常 。这个时候我们需要继续按照顺时针的方案,把缓存的数据放在用户A上面 。