Java一致性Hash算法的实现详解

 更新时间:2024年01月26日 09:45:22   作者:ZhaoJuFei  
这篇文章主要介绍了Java一致性Hash算法的实现详解,hash的意思是散列,目的将一组输入的数据均匀的分开、打散,往往用来配合路由算法做负载均衡,多用在分布式系统中,需要的朋友可以参考下

 哈希hash

hash的意思是散列,目的将一组输入的数据均匀的分开、打散,往往用来配合路由算法做负载均衡,多用在分布式系统中。

比如memcached它只提供了K V的存储、读取,如果使用了多台memcache做一个“逻辑集群”,就需要客户端做“路由算法”,来保证数据均匀的进去,然后能“原路”拿出来。

常规哈希取模

常规哈希,往往结合取模运算,以便将请求转发到后端的服务器上,如下图:

第一步使用hash算法,将请求“打散”得到一个整数(比如传递过来一个请求,使用jdk类库的hash对某个参数做计算),第二步将得到的参数对后端的服务器台数取模,以上图为例,加上有三台服务器,那么id分别为1~6的请求会被转发到1,2,0,1,2,0,上,不管请求id数是多少,总是这么周而复始的转发。

假设上面是个缓存系统,以上请求为set请求,在服务器数量不变的情况下,对同样的id做get请求,由于采用同样的hash算法,那么肯定能原路找到对应的key值。这个算法简单,而且数据分散的均匀。

如果系统访问量突增,为了扩容加了一台机器,编号为3,此时有了4台机器,采用同样的算法再去get请求会如何?比如id=6,这个时候 6%4=2,我们知道set时值其实放进了索引为0的机器,这个时候就get不到了。这就是上面算法的弊端,在增减机器时会使旧的数据大量“失效”,也就是命中率下降。

不带虚拟节点的一致性哈希算法

为了解决以上问题,聪明的人发明了一致性哈希算法。思路是这样,hash算法出来的整数有个范围,我们在这个范围内布置三台服务器(范围具体是多少看前面的hash算法)。假设hash的范围是1~300,每台负责一段范围内的请求,比如一台负责(1~100],一台负责(100~200],一台负责(200~1]。这三台server收尾相接覆盖/闭环了所有请求,称为哈希环,如下图:

如何实现一台服务器接收一个范围的请求?这个时候不用取模了,而是将server也按照hash算法计算一个id值,比如按照他们的ip+port+name拼成的串计算,假设正好分别是 1,100,200,将他们放进一个treeMap里,Map<Inetger,Node> ,其中Node代表server节点,是自定义的数据结构,比如是一个类,包含ip,port,name等属性。我们的例子中,map里包含三个元素。

一个请求过来hash得到的值必属于这三个server的范围,比如一个请求id=N,那么从map里get(N)去找server,找到直接转发,找不到进行如下运算:treemap里有个关键的api,tailMap(),这个接口能够返回id比N大的map的子集,然后取子集的第一个节点,就是id=100的节点,通常称为顺时针查找。

//得到应当路由到的结点(示例代码用String代表的节点)
private static String getServer(String key) {
	//得到该key的hash值
	int hash = getHash(key);
	//得到大于该Hash值的所有Map
	SortedMap<Integer, String> subMap = sortedMap.tailMap(hash);
	if(subMap.isEmpty()){
		//如果没有比该key的hash值大的,则从第一个node开始
		Integer i = sortedMap.firstKey();
		//返回对应的服务器
		return sortedMap.get(i);
	}else{
		//第一个Key就是顺时针过去离node最近的那个结点
		Integer i = subMap.firstKey();
		//返回对应的服务器
		return subMap.get(i);
	}
}

当然如果子集为空,这意味着N>200,就取整个map的第一个节点,完成闭环。

分析:从实现可以看出,如果一个节点挂了,他的流量会顺时针(逆时针实现也是一样的)“导流”到下一个节点,其他节点不受影响。假如有100台服务器,一台挂了,其他99台都能正常命中!这个算法比简单的取模好了很多。

不过这里仍有个问题,假设各台服务器性能差不多,此时流量突增,一台server由于流量过载而挂掉,那么它的下一台因为承载了2倍的流量,很有可能也会挂掉,依此类推,最后所有的节点都会挂掉,造成“雪崩”!

因此正常情况下,我们往往采用带虚拟节点的一致性哈希算法(不特别说明的一致性哈希算法一般都是指的带虚拟节点的算法)。

带虚拟节点的一致性哈希算法

带虚拟节点的一致性哈希算法是为了解决不带虚拟节点算法的雪崩问题,虚拟节点也称为分片。在上一步的基础上理解虚拟节点是非常容易的。“虚拟”节点是server的副本、分身,每个虚拟节点存储的server信息还是后面的物理地址,只不过每个server由一台变成了多台,这个时候往treeMap放节点时往往这么做:

for(i=1  -->  N) // N为每个server对应的分片数量
{
   Map.put(hash(ip+port+name+i),node) // 所有虚拟节点放进去
}
这个for循环外面还会有个循环,处理所有server node

由于每个server的ip,name不同,所以以上拼串hash后的值碰撞的概率是很小的,这样所有的虚拟节点也会离散的分部到环上,形成的hash环如下图,同样颜色的虚拟节点同属于一个server。

这个时候如果红颜色的server挂了,它的虚拟节点负责的范围会分别导航到下一个虚拟节点上,这些虚拟节点分别属于不同的server,就避免了流量全部导流到一台机器上。由于流量被均摊了,有效的减少了雪崩发生的概率。(理论上仍存在虚拟节点后面的虚拟节点属于同一个server的情况,但是当虚拟节点非常多时,这个概率是非常小的,而且这个分片数量是自定义的,往往设置几百个)。

只要是hash算法,就有哈希碰撞的可能性,在增加server时,计算后的虚拟节点跟其他server的虚拟节点重复的话,也会导致部分缓存失效(可以通过算法改良)。

综上,一致性哈希算法并不是强一致性,也不是高可用方案,如果server挂了数据丢了就是丢了,除非有恢复手段,它只是一种减少由扩缩容引起的命中率下降的手段。

到此这篇关于Java一致性Hash算法的实现详解的文章就介绍到这了,更多相关Java一致性Hash算法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java中的static关键字全面解析

    Java中的static关键字全面解析

    这篇文章主要介绍了Java中的static关键字全面解析的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2016-06-06
  • cmd中javac和java使用及注意事项详解

    cmd中javac和java使用及注意事项详解

    这篇文章主要介绍了cmd中javac和java使用及注意事项详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-07-07
  • SpringBoot 下的 Static 文件夹打包成前端资源的示例代码

    SpringBoot 下的 Static 文件夹打包成前端资源的示例代码

    这篇文章主要介绍了SpringBoot 下的 Static 文件夹如何打包成前端资源,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-06-06
  • SpringBoot+Vue实现动态菜单的思路梳理

    SpringBoot+Vue实现动态菜单的思路梳理

    这篇文章主要为大家详细介绍了利用SpringBoot+Vue实现动态菜单的思路梳理,文中的示例代码讲解详细,感兴趣的小伙伴可以动手尝试一下
    2022-07-07
  • Spring使用IOC与DI实现完全注解开发

    Spring使用IOC与DI实现完全注解开发

    IOC也是Spring的核心之一了,之前学的时候是采用xml配置文件的方式去实现的,后来其中也多少穿插了几个注解,但是没有说完全采用注解实现。那么这篇文章就和大家分享一下,全部采用注解来实现IOC + DI
    2022-09-09
  • Java中FileOutputStream类的使用

    Java中FileOutputStream类的使用

    java.io.FileOutputStream类是文件输出流,用于将数据写出到文件,下面就来介绍一下Java中FileOutputStream类的使用,具有一定的参考价值,感兴趣的可以了解一下
    2023-10-10
  • Java深入分析动态代理

    Java深入分析动态代理

    动态代理指的是,代理类和目标类的关系在程序运行的时候确定的,客户通过代理类来调用目标对象的方法,是在程序运行时根据需要动态的创建目标类的代理对象。本文将通过案例详细讲解一下Java动态代理的原理及实现,需要的可以参考一下
    2022-07-07
  • Java中this的用法实例总结

    Java中this的用法实例总结

    JAVA中的this是一个非常重要的模块,在编程中有非常重要的地位,擅长用this的人常常可以使程序更加简洁和方便,下面这篇文章主要给大家介绍了关于Java中this用法的相关资料,需要的朋友可以参考下
    2022-08-08
  • Java shiro安全框架使用介绍

    Java shiro安全框架使用介绍

    安全管理是软件系统必不可少的的功能。根据经典的“墨菲定律”——凡是可能,总会发生。如果系统存在安全隐患,最终必然会出现问题,这篇文章主要介绍了SpringBoot安全管理Shiro框架的使用
    2022-08-08
  • MyBatisPlus PaginationInterceptor分页插件的使用详解

    MyBatisPlus PaginationInterceptor分页插件的使用详解

    这篇文章主要介绍了MyBatisPlus PaginationInterceptor分页插件的使用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03

最新评论