一致性hash解决的问题

在解决分布式系统中负载均衡的问题时候可以使用Hash算法让固定的一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求的信息),起到负载均衡的作用。

但是普通的余数hash(hash(比如用户id)%服务器机器数)算法伸缩性很差,当新增或者下线服务器机器时候,用户id与服务器的映射关系会大量失效。一致性hash则利用hash环对其进行了改进。

一致性hash的特性

①,单调性

单调性(Monotonicity),单调性是指如果已经有一些请求通过哈希分派到了相应的服务器进行处理,又有新的服务器加入到系统中时候,应保证原有的请求可以被映射到原有的或者新的服务器中去,而不会被映射到原来的其它服务器上去。

②,分散性
一个用户的请求被路由到不同的服务器进行处理。好的哈希算法应尽量避免尽量降低分散性。 一致性hash具有很低的分散性
③,平衡性
致性hash可以做到每个服务器都进行处理请求,但是不能保证每个服务器处理的请求的数量大致相同,这里就要引入虚拟节点来解决这个问题

原文:https://blog.csdn.net/u011305680/article/details/79721030

参考: https://www.jianshu.com/p/e968c081f563

1、不带虚拟节点的

package hash;  

import java.util.SortedMap;
import java.util.TreeMap; /**
* 不带虚拟节点的一致性Hash算法
* 重点:1.如何造一个hash环,2.如何在哈希环上映射服务器节点,3.如何找到对应的节点
*/
public class ConsistentHashingWithoutVirtualNode { //待添加入Hash环的服务器列表
private static String[] servers = { "192.168.0.0:111", "192.168.0.1:111",
"192.168.0.2:111", "192.168.0.3:111", "192.168.0.4:111" }; //key表示服务器的hash值,value表示服务器
private static SortedMap<Integer, String> sortedMap = new TreeMap<Integer, String>(); //程序初始化,将所有的服务器放入sortedMap中
static {
for (int i=0; i<servers.length; i++) {
int hash = getHash(servers[i]);
System.out.println("[" + servers[i] + "]加入集合中, 其Hash值为" + hash);
sortedMap.put(hash, servers[i]);
}
System.out.println();
} //得到应当路由到的结点
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);
}
} //使用FNV1_32_HASH算法计算服务器的Hash值,这里不使用重写hashCode的方法,最终效果没区别
private static int getHash(String str) {
final int p = 16777619;
int hash = (int) 2166136261L;
for (int i = 0; i < str.length(); i++)
hash = (hash ^ str.charAt(i)) * p;
hash += hash << 13;
hash ^= hash >> 7;
hash += hash << 3;
hash ^= hash >> 17;
hash += hash << 5; // 如果算出来的值为负数则取其绝对值
if (hash < 0)
hash = Math.abs(hash);
return hash;
} public static void main(String[] args) {
String[] keys = {"太阳", "月亮", "星星"};
for(int i=0; i<keys.length; i++)
System.out.println("[" + keys[i] + "]的hash值为" + getHash(keys[i])
+ ", 被路由到结点[" + getServer(keys[i]) + "]");
}
}

2、带虚拟节点的

package hash;  

import java.util.LinkedList;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap; import org.apache.commons.lang.StringUtils; /**
* 带虚拟节点的一致性Hash算法
*/
public class ConsistentHashingWithoutVirtualNode { //待添加入Hash环的服务器列表
private static String[] servers = {"192.168.0.0:111", "192.168.0.1:111", "192.168.0.2:111",
"192.168.0.3:111", "192.168.0.4:111"}; //真实结点列表,考虑到服务器上线、下线的场景,即添加、删除的场景会比较频繁,这里使用LinkedList会更好
private static List<String> realNodes = new LinkedList<String>(); //虚拟节点,key表示虚拟节点的hash值,value表示虚拟节点的名称
private static SortedMap<Integer, String> virtualNodes = new TreeMap<Integer, String>(); //虚拟节点的数目,这里写死,为了演示需要,一个真实结点对应5个虚拟节点
private static final int VIRTUAL_NODES = 5; static{
//先把原始的服务器添加到真实结点列表中
for(int i=0; i<servers.length; i++)
realNodes.add(servers[i]); //再添加虚拟节点,遍历LinkedList使用foreach循环效率会比较高
for (String str : realNodes){
for(int i=0; i<VIRTUAL_NODES; i++){
String virtualNodeName = str + "&&VN" + String.valueOf(i);
int hash = getHash(virtualNodeName);
System.out.println("虚拟节点[" + virtualNodeName + "]被添加, hash值为" + hash);
virtualNodes.put(hash, virtualNodeName);
}
}
System.out.println();
} //使用FNV1_32_HASH算法计算服务器的Hash值,这里不使用重写hashCode的方法,最终效果没区别
private static int getHash(String str){
final int p = 16777619;
int hash = (int)2166136261L;
for (int i = 0; i < str.length(); i++)
hash = (hash ^ str.charAt(i)) * p;
hash += hash << 13;
hash ^= hash >> 7;
hash += hash << 3;
hash ^= hash >> 17;
hash += hash << 5; // 如果算出来的值为负数则取其绝对值
if (hash < 0)
hash = Math.abs(hash);
return hash;
} //得到应当路由到的结点
private static String getServer(String key){
//得到该key的hash值
int hash = getHash(key);
// 得到大于该Hash值的所有Map
SortedMap<Integer, String> subMap = virtualNodes.tailMap(hash);
String virtualNode;
if(subMap.isEmpty()){
//如果没有比该key的hash值大的,则从第一个node开始
Integer i = virtualNodes.firstKey();
//返回对应的服务器
virtualNode = virtualNodes.get(i);
}else{
//第一个Key就是顺时针过去离node最近的那个结点
Integer i = subMap.firstKey();
//返回对应的服务器
virtualNode = subMap.get(i);
}
//virtualNode虚拟节点名称要截取一下
if(StringUtils.isNotBlank(virtualNode)){
return virtualNode.substring(0, virtualNode.indexOf("&&"));
}
return null;
} public static void main(String[] args){
String[] keys = {"太阳", "月亮", "星星"};
for(int i=0; i<keys.length; i++)
System.out.println("[" + keys[i] + "]的hash值为" +
getHash(keys[i]) + ", 被路由到结点[" + getServer(keys[i]) + "]");
}
}

一致性hash的实现的更多相关文章

  1. 对一致性Hash算法,Java代码实现的深入研究

    一致性Hash算法 关于一致性Hash算法,在我之前的博文中已经有多次提到了,MemCache超详细解读一文中"一致性Hash算法"部分,对于为什么要使用一致性Hash算法.一致性 ...

  2. 转载自lanceyan: 一致性hash和solr千万级数据分布式搜索引擎中的应用

    一致性hash和solr千万级数据分布式搜索引擎中的应用 互联网创业中大部分人都是草根创业,这个时候没有强劲的服务器,也没有钱去买很昂贵的海量数据库.在这样严峻的条件下,一批又一批的创业者从创业中获得 ...

  3. 一致性hash算法详解

    转载请说明出处:http://blog.csdn.net/cywosp/article/details/23397179     一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT) ...

  4. 探索c#之一致性Hash详解

    阅读目录: 使用场景 算法原理 虚拟节点 代码示例 使用场景 以Redis为例,当系统需要缓存的内容超过单机内存大小时,例如要缓存100G数据,单机内存仅有16G时.这时候就需要考虑进行缓存数据分片, ...

  5. 一致性hash算法简介

    一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似.一致性哈希修正了CARP使用的简单哈希 ...

  6. 分布式缓存技术memcached学习(四)—— 一致性hash算法原理

    分布式一致性hash算法简介 当你看到“分布式一致性hash算法”这个词时,第一时间可能会问,什么是分布式,什么是一致性,hash又是什么.在分析分布式一致性hash算法原理之前,我们先来了解一下这几 ...

  7. 关于Memcached一致性hash的探究

    参考文章 http://blog.chinaunix.net/uid-20498361-id-4303232.html http://blog.csdn.net/kongqz/article/deta ...

  8. 一致性 hash 算法( consistent hashing )a

    一致性 hash 算法( consistent hashing ) 张亮 consistent hashing 算法早在 1997 年就在论文 Consistent hashing and rando ...

  9. Ceph剖析:数据分布之CRUSH算法与一致性Hash

    作者:吴香伟 发表于 2014/09/05 版权声明:可以任意转载,转载时务必以超链接形式标明文章原始出处和作者信息以及版权声明 数据分布是分布式存储系统的一个重要部分,数据分布算法至少要考虑以下三个 ...

  10. 一致性Hash算法

    from wikipedia 一致哈希 历史 1997年由MIT的Karger等在一篇学术论文中提出如何将“一致性Hash”应用于用户易变的分布式Web服务中.也可用于实现健壮缓存来减少大型Web应用 ...

随机推荐

  1. PHP 合并有序数组

    <?php //两个有序数组合并 $arr1 = [1,5,7,44,66,89]; $arr2 = [4,5,6,88,99,105,111]; $arr3 = []; $l1 = count ...

  2. An error occurred. Sorry, the page you are looking for is currently unavailable. Please try again later.

    刚装完 PHP.Nginx,准备跑下 phpMyAdmin 程序,结果报以下错误: An error occurred. Sorry, the page you are looking for is ...

  3. ES6 export

    一.默认导出(default export)// 1.一个模块只能有一个默认导出, 对于默认导出, 导入的名称可以和导出的名称不一致, 这对于导出匿名函数或类非常有用. <!---- page. ...

  4. 联想R720面板右下部分按压后和上面按键串联了

    如图所示的位置,一用力按压,就会触发键盘的按键. 前提: 本人刚刚加装了内存条,内存条是京东买的 十铨(Team) DDR4 2400 8GB 笔记本内存,安装完内存以后,发现电脑出了这样的问题. 解 ...

  5. SharePoint Server 2019新特性

    .基于.NET4.7框架 功能基本是把office365的SPO功能搬家过来.如下: Sharepoint server 2019将在2018年发布 New web parts There were ...

  6. Mac - 如何安全地还原 Mac 的默认字体

    为清理mac系统内存,下载了daisydisk for mac 破解版,然后发现mac所有的字体都被清理了,所有汉子都变成了问号❓和方框.... 在通常情况下,遇到字体显示不正常,甚至乱码时,重置总是 ...

  7. Codeforces Round 504

    (交互题真神奇,,,我自己瞎写了一发目测样例都没过去就AC了...) (只出了两题的竟然没掉下蓝名真是可怕) A:我的代码太不美观了,放个同学的(因为我是c++63分的蒟蒻所以根本不知道那些函数怎么用 ...

  8. 把html页面转化成图片——html2canvas

    test.html <div class="fx_zhezhao"></div> <div class="myImg"> & ...

  9. Oracle 修改表 Alter Table...

    --增加列ALTER TABLE Student add sex number(2);--删除列ALTER TABLE Student drop column sex;--更改列属性 ALTER TA ...

  10. 目前常用的加密算法有DES(Data Encryption Standard)和IDEA(International Data Encryption Algorithm)国际数据加密算法等,请用工厂方法实现加密算法系统。提交该系统的代码,该系统务必是一个可以能够直接使用的系统,查阅资料完成相应加密算法的实现;

    1.加密算法的类图结构 2.源代码 2.1代码运行截图 2.2代码的目录结构 2.3具体代码 MethodFactory.java package jiami; public interface Me ...