一文看懂一致性Hash算法哈希算法原理解析,如何利用哈希函数预测博彩走势

2025-07-31

  哈希算法,SHA256,哈希函数,加密哈希,哈希预测/哈希算法是博彩游戏公平性的核心,本文详细解析 SHA256 哈希函数的运作原理,并提供如何通过哈希技术进行博彩预测的方法!导语 一致性Hash算法是解决分布式缓存等问题的一种算法; 本文介绍了一致性Hash算法的原理,并给出了一种实现和实际运用的案例;

  我们有三台缓存服务器编号node0、node1、node2,现在有3000万个key,希望可以将这些个key均匀的缓存到三台机器上,你会想到什么方案呢?

  我们可能首先想到的方案是:取模算法hash(key)% N,即:对key进行hash运算后取模,N是机器的数量;

  这样,对key进行hash后的结果对3取模,得到的结果一定是0、1或者2,正好对应服务器node0、node1、node2,存取数据直接找对应的服务器即可,简单粗暴,完全可以解决上述的问题;

  取模算法虽然使用简单,但对机器数量取模,在集群扩容和收缩时却有一定的局限性:因为在生产环境中根据业务量的大小,调整服务器数量是常有的事;

  而服务器数量N发生变化后hash(key)% N计算的结果也会随之变化!

  比如:一个服务器节点挂了,计算公式从hash(key)% 3变成了hash(key)% 2,结果会发生变化,此时想要访问一个key,这个key的缓存位置大概率会发生改变,那么之前缓存key的数据也会失去作用与意义;

  大量缓存在同一时间失效,造成缓存的雪崩,进而导致整个缓存系统的不可用,这基本上是不能接受的;

  一致性Hash算法详述算法原理一致性哈希算法在 1997 年由麻省理工学院提出,是一种特殊的哈希算法,在移除或者添加一个服务器时,能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系; 一致性哈希解决了简单哈希算法在分布式哈希表(Distributed Hash Table,DHT)中存在的动态伸缩等问题;

  不过,不同于上边按服务器数量取模,一致性hash是对固定值2^32取模;

  IPv4的地址是4组8位2进制数组成,所以用2^32可以保证每个IP地址会有唯一的映射;① hash环

  我们可以将这2^32个值抽象成一个圆环⭕️,圆环的正上方的点代表0,顺时针排列,以此类推:1、2、3…直到2^32-1,而这个由2的32次方个点组成的圆环统称为hash环;

  使用服务器IP地址进行hash计算,用哈希后的结果对2^32取模,结果一定是一个0到2^32-1之间的整数;

  而这个整数映射在hash环上的位置代表了一个服务器,依次将node0、node1、node2三个缓存服务器映射到hash环上;

  在对对应的Key映射到具体的服务器时,需要首先计算Key的Hash值:hash(key)% 2^32;

  注:此处的Hash函数可以和之前计算服务器映射至Hash环的函数不同,只要保证取值范围和Hash环的范围相同即可(即:2^32);

  从缓存对象key的位置开始,沿顺时针方向遇到的第一个服务器,便是当前对象将要缓存到的服务器;

  首先,使用哈希函数计算这个对象的 hash 值,值的范围是 [0, 2^32-1]:

  可以看到,一致性Hash就是:将原本单个点的Hash映射,转变为了在一个环上的某个片段上的映射!

  假设 CS3 服务器出现故障导致服务下线,这时原本存储于 CS3 服务器的对象 o4,需要被重新分配至 CS2 服务器,其它对象仍存储在原有的机器上:

  假如业务量激增,我们需要增加一台服务器 CS4,经过同样的 hash 运算,该服务器最终落于 t1 和 t2 服务器之间,具体如下图所示:

  在以上示例中只有 o3 对象需要重新分配,即它被重新到 CS4 服务器;

  在前面我们已经说过:如果使用简单的取模方法,当新添加服务器时可能会导致大部分缓存失效,而使用一致性哈希算法后,这种情况得到了较大的改善,因为只有少部分对象需要重新分配!

  但是在实际场景中很难选取到一个Hash函数这么完美的将各个服务器散列到Hash环上;

  此时,在服务器节点数量太少的情况下,很容易因为节点分布不均匀而造成数据倾斜问题;

  如下图被缓存的对象大部分缓存在node-4服务器上,导致其他节点资源浪费,系统压力大部分集中在node-4节点上,这样的集群是非常不健康的:

  在上面新增服务器 CS4 时,CS4 只分担了 CS1 服务器的负载,服务器 CS2 和 CS3 并没有因为 CS4 服务器的加入而减少负载压力;如果 CS4 服务器的性能与原有服务器的性能一致甚至可能更高,那么这种结果并不是我们所期望的;

  即将每台物理服务器虚拟为一组虚拟服务器,将虚拟服务器放置到哈希环上,如果要确定对象的服务器,需先确定对象的虚拟服务器,再由虚拟服务器确定物理服务器;

  在图中:o1 和 o2 表示对象,v1 ~ v6 表示虚拟服务器,s1 ~ s3 表示实际的物理服务器;

  虚拟节点的hash计算通常可以采用:对应节点的IP地址加数字编号后缀 hash(10.24.23.227#1) 的方式;

  一致性hash在分布式系统中应该是实现负载均衡的首选算法,它的实现比较灵活,既可以在客户端实现,也可以在中间件上实现,比如日常使用较多的缓存中间件memcached和redis集群都有用到它;

  memcached的集群比较特殊,严格来说它只能算是伪集群,因为它的服务器之间不能通信,请求的分发路由完全靠客户端来的计算出缓存对象应该落在哪个服务器上,而它的路由算法用的就是一致性hash;

  还有redis集群中hash槽的概念,虽然实现不尽相同,但思想万变不离其宗,看完本篇的一致性hash,你再去理解redis槽位就轻松多了;

  下面我们根据上面的讲述,使用Golang实现一个一致性Hash算法,这个算法具有一些下面的功能特性:

  defaultReplicaNum:默认情况下,每个真实的物理服务器在Hash环中虚拟节点的个数;

  这里用到的是SHA512算法,并取的是unsigned int64,这一点和上面介绍的0~2^32-1有所区别!

  这里的虚拟节点的格式为:%s%d,和上文提到的10.24.23.227#1的格式有所区别,但是道理是一样的!

  这里使用数组作为Hash环只是为了便于说明,在实际实现中建议选用其他数据结构进行实现,以获取更好的性能;

  当缓存服务器信息写入 replicaHostMap 映射以及 Hash 环后,即完成了缓存服务器的注册;

  和注册缓存服务器相反,将服务器在 Map 映射以及 Hash 环中去除即完成了注销;

  查询 Key 是整个一致性 Hash 算法的核心,但是实现起来也并不复杂;

  注意到,如果 key 比当前Hash环中最大的虚拟节点的 hash 值还大,则选择当前 Hash环 中 hash 值最小的一个节点(即“环形”的逻辑):

  为了简单起见,这里使用了 HTTP 服务器作为缓存服务器,具体代码如下所示:

  代码执行后,会调用 startServer 函数启动一个http服务器;

  在 startServer 函数中,首先调用 registerHost 在代理服务器上进行注册(下文会讲),并监听 /路径,具体代码如下:

  随后,查询服务器中的缓存(为了简单起见,这里使用 sync.Map 来模拟缓存):

  有了缓存服务器之后,我们还需要一个代理服务器来选择具体选择哪个缓存服务器来请求;

  代理服务器的逻辑很简单,就是创建一个一致性Hash结构: Consistent,把 Consistent 和请求缓存服务器的逻辑进行了一层封装;

  可以看到,8000端口的服务器对key值进行了缓存,并在10秒后清除了缓存;

  开启debug,并注册单个缓存服务器后,查看 Consistent 中的值:

  从debug中的变量,我们就可以很清楚的看到注册不同数量的服务器时,一致性Hash上服务器的动态变化!

  但是很多时候,我们的缓存服务器需要同时处理大量的缓存请求,而通过上面的算法,我们总是会去同一台缓存服务器去获取缓存数据;

  下面我们在基本的一致性Hash算法的基础上,实现含有负载边界值的一致性Hash!

  17年时,Google 提出了含有负载边界值的一致性Hash算法,此算法主要应用于在实现一致性的同时,实现负载的平均性;

  这个算法将缓存服务器视为一个含有一定容量的桶(可以简单理解为Hash桶),将客户端视为球,则平均性目标表示为:所有约等于平均密度(球的数量除以桶的数量):

  实际使用时,可以设定一个平均密度的参数 ε,将每个桶的容量设置为平均加载时间的 下上限 (1+ε);

  使用哈希函数将 6 个球和 3 个桶分配给 Hash环 上的随机位置,假设每个桶的容量设置为 2,按 ID 值的递增顺序分配球;

  在上面基本一致性 Hash 算法实现的基础上,我们继续实现含有负载边界值的一致性Hash算法;

  在核心算法中添加根据负载情况查询Key的函数,以及增加/释放负载值的函数;

  在 GetKeyLeast 函数中,首先根据 searchKey 函数,顺时针获取可能满足条件的第一个虚拟节点;

  随后调用 checkLoadCapacity 校验当前缓存服务器的负载数是否满足条件:

  如果不满足条件,则沿着 Hash 环走到下一个虚拟节点,继续判断是否满足条件,直到满足条件;

  这里使用的是无条件的 for 循环,因为一定存在低于 平均负载(1 + loadBoundFactor) 的虚拟节点!*

  可以看到,缓存被均摊到了其他服务器(这是由于一个缓存请求会持续10s导致的)!

  在此基础之上,根据 Google 的论文实现了带有负载边界的一致性Hash算法;

地址:广东省广州市天河区88号 客服热线:400-123-4567 传真:+86-123-4567 QQ:1234567890

Copyright © 2012-2025 哈希游戏推荐 版权所有 非商用版本