Java一致性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挂了数据丢了就是丢了,除非有恢复手段,它只是一种减少由扩缩容引起的命中率下降的手段。
代码可参考如下链接
https://blog.csdn.net/WANGYAN9110/article/details/70185652
https://blog.csdn.net/u010558660/article/details/52767218
————————————————
版权声明:本文为CSDN博主「赵举飞」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/flyfeifei66/article/details/82458618
Java一致性Hash算法的实现的更多相关文章
- Java 一致性Hash算法的学习
目前我们很多时候都是在做分布式系统,但是我们需把客户端的请求均匀的分布到N个服务器中,一般我们可以考虑通过Object的HashCodeHash%N,通过取余,将客户端的请求分布到不同的的服务端.但是 ...
- 几种经典的Hash算法的实现(源代码)
来源声明: http://blog.minidx.com/2008/01/27/446.html 先保存下来,以备后面研究,现在还看不懂! 哈希算法将任意长度的二进制值映射为固定长度的较小二进制值,这 ...
- 一致性Hash算法在数据库分表中的实践
最近有一个项目,其中某个功能单表数据在可预估的未来达到了亿级,初步估算在90亿左右.与同事详细讨论后,决定采用一致性Hash算法来完成数据库的自动扩容和数据迁移.整个程序细节由我同事完成,我只是将其理 ...
- 一致性hash与CRUSH算法总结
相同之处:都解决了数据缓存系统中数据如何存储与路由. 不同之处:区别在于虚拟节点和物理节点的映射办法不同 由于一般的哈希函数返回一个int(32bit)型的hashCode.因此,可以将该哈希函数能够 ...
- 一致性Hash算法(转载)
原文地址http://blog.csdn.net/caigen1988/article/details/7708806 consistent hashing 算法早在 1997 年就在论文 Con ...
- 不会一致性hash算法,劝你简历别写搞过负载均衡
大家好,我是小富~ 个人公众号:程序员内点事,欢迎学习交流 这两天看到技术群里,有小伙伴在讨论一致性hash算法的问题,正愁没啥写的题目就来了,那就简单介绍下它的原理.下边我们以分布式缓存中经典场景举 ...
- 对一致性Hash算法,Java代码实现的深入研究
一致性Hash算法 关于一致性Hash算法,在我之前的博文中已经有多次提到了,MemCache超详细解读一文中"一致性Hash算法"部分,对于为什么要使用一致性Hash算法.一致性 ...
- Java实现一致性Hash算法深入研究
一致性Hash算法 关于一致性Hash算法,在我之前的博文中已经有多次提到了,MemCache超详细解读一文中”一致性Hash算法”部分,对于为什么要使用一致性Hash算法和一致性Hash算法的算法原 ...
- java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现
java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析 ...
随机推荐
- flex 遇上white-space:nowrap的2种解决方法
需求:使用flex布局,超出部分想使用点点点显示 一.方法1使用min-width:0 效果: HTML代码如下: <div class="team-body"> &l ...
- 关于字符串中每个单词的首字母大写化问题之 拆分split(/\s+/)
var a = 'Hi, my name\'s Han Meimei, a SOFTWARE engineer'; //for循环 function titleCase(s) { var i, ss ...
- PHP读取TXT中文乱码的解决方法
//$fname文件名称 if ($fname = $_FILES['nickname']['tmp_name']) { //file_get_contents() 函数把整个文件读入一个字符串中. ...
- Second largest node in the BST
Find the second largest node in the BST 分析: 如果root有右节点,很明显第二大的node有可能在右子树里.唯一不满足的条件就是右子树只有一个node. 这个 ...
- Centos7 + nginx 托管 Django 项目
使用nginx托管django服务的原理 使用uwsgi开启django服务(通过配置文件启动) 防火墙关闭uwsgi端口(uwsgi的websocket一定要使用127.0.0.1的方式配置)) 编 ...
- 【LOJ】#3036. 「JOISC 2019 Day3」指定城市
LOJ#3036. 「JOISC 2019 Day3」指定城市 一个点的可以dp出来 两个点也可以dp出来 后面的就是在两个点的情况下选一条最长的链加进去,用线段树维护即可 #include < ...
- 首篇-记录自己学习python之路!
对于自己学习python的目的比较明确——爬虫和量化. 目前找了一些资源进行学习,先进行量化方面的学习,爬虫滞后.目前的目标是“180天掌握尽可能多的量化能力”! 以后定时发送自己学习思考内容以作自己 ...
- Win32汇编-编写PE结构解析工具
汇编语言(assembly language)是一种用于电子计算机.微处理器.微控制器或其他可编程器件的低级语言,亦称为符号语言.在汇编语言中,用助记符(Mnemonics)代替机器指令的操作码,用地 ...
- python读写增删修改ini配置文件
一,百度百科 .ini 文件是Initialization File的缩写,即初始化文件,是windows的系统配置文件所采用的存储格式,统管windows的各项配置,一般用户就用windows提供的 ...
- 手把手教你搭建FastDFS集群(下)
手把手教你搭建FastDFS集群(下) 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/u0 ...