一致哈希算法Java实现
一致哈希算法(Consistent Hashing Algorithms)是一个分布式系统中经常使用的算法。
传统的Hash算法当槽位(Slot)增减时,面临全部数据又一次部署的问题。而一致哈希算法确可以保证,仅仅须要移动K/n份数据(K为数据总量, n为槽位数量),且仅仅影响现有的当中一个槽位。
这使得分布式系统中面对新增或者删除机器时。可以更高速的处理更改请求。
本文将用Java实现一个简单版本号的一致哈希算法,仅仅为说明一致哈希算法的核心思想。
一致哈希算法介绍
一致哈希算法的介绍非常多。如wiki,以及非常多的博客。在此仅仅简述其概念。具体的介绍请參考相关论文。
第一个概念是节点(Node),分布式系统中相当于一台机器。全部的节点逻辑上围起来形成一个圆环。第二个概念是数据(Data)。每份数据都有一个key值。数据总是须要存储到某一个节点上。数据和节点之间怎样关联的呢?通过区域的概念关联。每个节点都负责圆环上的一个区域,落在该区域上的就存储在该节点上,通常区域为左側闭合。右側开放的形式。如[2500,5000)。
下面是一个拥有4个节点的一致哈希算法示意图:
总的范围定为10000,也限定了总槽位的数量。能够按照项目的须要,制定合适的大小。
- Node1的起始位置为0。负责存储[0, 2500)之间的数据
- Node2的起始位置为2500,负责存储[2500, 5000)之间的数据
- Node3的起始位置为5000。负责存储[5000, 7500)之间的数据
- Node4的起始位置为7500,负责存储[7500, 10000)之间的数据
一致哈希算法最大的特点在于新增或者删除节点的处理。
假设新增一个起始位置为1250的节点Node5,那么影响到的唯一一个节点就是Node1,Node1的存储范围由[0, 2500)变更[0, 1250)。Node5的存储范围为[1250, 2500),所以须要把落于[1250, 2500)范围的数据搬移到Node5上。其他的不须要做出改变,这一点很的重要。相当于Node5分担了Node1的部分工作。假设把Node3删除,那么须要把Node3上面的数据搬移到Node2上面,Node2的范围扩大为[2500,7500),Node2承担了Node3的工作。
一致哈希算法Java的详细实现
Java是面向对象的语言,首先须要抽象对象。Node。表示节点,有名字。起始位置,以及数据列表三个属性,因为Node和数据之间的匹配。使用的是范围,所以为了简单起见,Node上加了一个end的属性。本来应该有Data以及DataKey的概念。可是为了简单起见,演示样例中Data就是字符串,Key就是自己。
整个圆环有一个长度,定义为scope,默觉得10000。
新增节点的算法是。找到最大的空挡。把新增节点放中间。当然也能够换为找到压力(数据量)最大的节点,把新增节点放在该节点之后。删除节点有一点小技巧。假设删除的是開始位置为0的节点,那么把下一个节点的開始位置置为0,和普通的退格不同。
这能保证仅仅要有节点。就一定有一个从0開始的节点。这能简化我们的算法和处理逻辑。
addItem方法就是往系统里面放数据,最后为了展示数据的分布效果,提供desc方法。打印出数据分布情况。非常有意思。
总体代码例如以下:
public class ConsistentHash {
private int scope = 10000;
private List<Node> nodes; public ConsistentHash() {
nodes = new ArrayList<Node>();
} public int getScope() {
return scope;
} public void setScope(int scope) {
this.scope = scope;
} public void addNode(String nodeName) {
if (nodeName == null || nodeName.trim().equals("")) {
throw new IllegalArgumentException("name can't be null or empty");
} if (containNodeName(nodeName)) {
throw new IllegalArgumentException("duplicate name");
} Node node = new Node(nodeName);
if (nodes.size() == 0) {
node.setStart(0);
node.setEnd(scope);
nodes.add(node);
} else {
Node maxNode = getMaxSectionNode();
int middle = maxNode.start + (maxNode.end - maxNode.start) / 2; node.start = middle;
node.end = maxNode.end;
int maxPosition = nodes.indexOf(maxNode);
nodes.add(maxPosition + 1, node); maxNode.setEnd(middle); // move data
Iterator<String> iter = maxNode.datas.iterator();
while (iter.hasNext()) {
String data = iter.next();
int value = Math.abs(data.hashCode()) % scope;
if (value >= middle) {
iter.remove();
node.datas.add(data);
}
}
for (String data : maxNode.datas) {
int value = Math.abs(data.hashCode()) % scope;
if (value >= middle) {
maxNode.datas.remove(data);
node.datas.add(data);
}
}
}
} public void removeNode(String nodeName) {
if (!containNodeName(nodeName)) {
throw new IllegalArgumentException("unknown name");
} if (nodes.size() == 1 && nodes.get(0).datas.size() > 0) {
throw new IllegalArgumentException("last node, and still have data");
} Node node = findNode(nodeName);
int position = nodes.indexOf(node);
if (position == 0) {
if (nodes.size() > 1) {
Node newFirstNode = nodes.get(1);
for (String data : node.datas) {
newFirstNode.datas.add(data);
}
newFirstNode.setStart(0);
}
} else {
Node lastNode = nodes.get(position - 1);
for (String data : node.datas) {
lastNode.datas.add(data);
}
lastNode.setEnd(node.end);
}
nodes.remove(position);
} public void addItem(String item) {
if (item == null || item.trim().equals("")) {
throw new IllegalArgumentException("item can't be null or empty");
} int value = Math.abs(item.hashCode()) % scope;
Node node = findNode(value);
node.datas.add(item);
} public void desc() {
System.out.println("Status:");
for (Node node : nodes) {
System.out.println(node.name + ":(" + node.start + "," + node.end
+ "): " + listString(node.datas));
}
} private String listString(LinkedList<String> datas) {
StringBuffer buffer = new StringBuffer();
buffer.append("{");
Iterator<String> iter = datas.iterator();
if (iter.hasNext()) {
buffer.append(iter.next());
} while (iter.hasNext()) {
buffer.append(", " + iter.next());
}
buffer.append("}");
return buffer.toString();
} private boolean containNodeName(String nodeName) {
if (nodes.isEmpty()) {
return false;
} Iterator<Node> iter = nodes.iterator();
while (iter.hasNext()) {
Node node = iter.next();
if (node.name.equals(nodeName)) {
return true;
}
} return false;
} private Node findNode(int value) {
Iterator<Node> iter = nodes.iterator();
while (iter.hasNext()) {
Node node = iter.next();
if (value >= node.start && value < node.end) {
return node;
}
} return null;
} private Node findNode(String nodeName) {
Iterator<Node> iter = nodes.iterator();
while (iter.hasNext()) {
Node node = iter.next();
if (node.name.equals(nodeName)) {
return node;
}
} return null;
} private Node getMaxSectionNode() {
if (nodes.size() == 1) {
return nodes.get(0);
} Iterator<Node> iter = nodes.iterator();
int maxSection = 0;
Node maxNode = null;
while (iter.hasNext()) {
Node node = iter.next();
int section = node.end - node.start;
if (section > maxSection) {
maxNode = node;
maxSection = section;
}
} return maxNode;
} static class Node {
private String name;
private int start;
private int end;
private LinkedList<String> datas; public Node(String name) {
this.name = name;
datas = new LinkedList<String>();
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getStart() {
return start;
} public void setStart(int start) {
this.start = start;
} public int getEnd() {
return end;
} public void setEnd(int end) {
this.end = end;
} public LinkedList<String> getDatas() {
return datas;
} public void setDatas(LinkedList<String> datas) {
this.datas = datas;
}
} public static void main(String[] args) {
ConsistentHash hash = new ConsistentHash();
hash.addNode("Machine-1");
hash.addNode("Machine-2");
hash.addNode("Machine-3");
hash.addNode("Machine-4"); hash.addItem("Hello");
hash.addItem("hash");
hash.addItem("main");
hash.addItem("args");
hash.addItem("LinkedList");
hash.addItem("end"); hash.desc(); hash.removeNode("Machine-1"); hash.desc(); hash.addNode("Machine-5"); hash.desc(); hash.addItem("scheduling");
hash.addItem("queue");
hash.addItem("thumb");
hash.addItem("quantum");
hash.addItem("approaches");
hash.addItem("migration");
hash.addItem("null");
hash.addItem("feedback");
hash.addItem("ageing");
hash.addItem("bursts");
hash.addItem("shorter"); hash.desc(); hash.addNode("Machine-6");
hash.addNode("Machine-7");
hash.addNode("Machine-8"); hash.desc(); hash.addNode("Machine-9");
hash.addNode("Machine-10");
hash.addNode("Machine-11"); hash.desc(); hash.addNode("Machine-12");
hash.addNode("Machine-13");
hash.addNode("Machine-14");
hash.addNode("Machine-15");
hash.addNode("Machine-16");
hash.addNode("Machine-17"); hash.desc();
} }
须要进一步完好的地方
不同节点之间互相备份,提高系统的可靠性。节点范围的动态调整。有时候分布可能不够平衡。
一致哈希算法Java实现的更多相关文章
- java单向加密算法小结(2)--MD5哈希算法
上一篇文章整理了Base64算法的相关知识,严格来说,Base64只能算是一种编码方式而非加密算法,这一篇要说的MD5,其实也不算是加密算法,而是一种哈希算法,即将目标文本转化为固定长度,不可逆的字符 ...
- Java_一致性哈希算法与Java实现
摘自:http://blog.csdn.net/wuhuan_wp/article/details/7010071 一致性哈希算法是分布式系统中常用的算法.比如,一个分布式的存储系统,要将数据存储到具 ...
- 一致性哈希算法学习及JAVA代码实现分析
1,对于待存储的海量数据,如何将它们分配到各个机器中去?---数据分片与路由 当数据量很大时,通过改善单机硬件资源的纵向扩充方式来存储数据变得越来越不适用,而通过增加机器数目来获得水平横向扩展的方式则 ...
- 一致性哈希算法原理及Java实现
一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似.一致性哈希修正了CARP使用的简 单 ...
- 感知哈希算法的java实现
一.原理讲解 实现这种功能的关键技术叫做"感知哈希算法"(Perceptual Hash Algorithm), 意思是为图片生成一个指纹(字符串格式), 两张图片的指纹 ...
- 一致性哈希算法介绍,及java实现
应用场景 在做服务器负载均衡时候可供选择的负载均衡的算法有很多,包括: 轮循算法(Round Robin).哈希算法(HASH).最少连接算法(Least Connection).响应速度算法(Res ...
- 一致性哈希算法原理、避免数据热点方法及Java实现
一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似.一致性哈希修正了CARP使用的简 单 ...
- 哈希算法原理【Java实现】(十)
前言 在入学时,学校为我们每位童鞋建立一个档案信息,当然每个档案信息都对应档案编号,还有比如在学校图书馆,图书馆为每本书都编了唯一的一个书籍号,那么问题来了,当我们需要通过档案号快速查到对应档案信息或 ...
- 一致性哈希算法与Java实现
原文:http://blog.csdn.net/wuhuan_wp/article/details/7010071 一致性哈希算法是分布式系统中常用的算法.比如,一个分布式的存储系统,要将数据存储到具 ...
随机推荐
- 69.nodejs对mongodb数据库的增删改查操作
转自:https://www.cnblogs.com/sexintercourse/p/6485381.html 首先要确保mongodb的正确安装,安装参照:http://docs.mongodb. ...
- 类数组对象arguments 和 数组对象
arguments并不是一个真正的数组,而是一个“类似数组(array-like)”的对象: 就像下面的这段输出,就是典型的类数组对象: {0:12, 1:23} 一.类数组 VS 数组 相同点: 都 ...
- deep-in-es6(六)
箭头函数 Arrow Functions 箭头函数在JavaScript诞生是就存在,JavaScript建议在HTML注释内包裹行内脚本,这样可以避免不支持JS代码的浏览器将JS显示为文本. < ...
- 【2017 Multi-University Training Contest - Team 2】 Is Derek lying?
[Link]: [Description] 两个人都做了完全一样的n道选择题,每道题都只有'A','B','C' 三个选项,,每道题答对的话得1分,答错不得分也不扣分,告诉你两个人全部n道题各自选的是 ...
- Dubbo学习总结(3)——Dubbo-Admin管理平台和Zookeeper注册中心的搭建
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件.它是一个为分布式应用提供一致性服务的软件,提供的功 ...
- opencv标定程序(改动)
转载请注明来自:http://blog.csdn.net/zhouyelihua/article/details/38421377 资源下载见:点击打开链接 百度云盘免积分下载:https://pan ...
- Css3 过渡(Transition)特效回调函数
Css3 出来之后,能够说是替代了Flash,通过使用Html5和Css3的完美结合.就能够做出不论什么你想得到的特效,这里不再阐述... 近期在做一个喝水签到的功能.在想签到成功之后,签到框能够模拟 ...
- js18--继承方式
方式1:子类.prototype = 父类对象 Boy.prototype = new Person(); Sub.prototype = new Sup('张三'); //可以传参数也可以不传 ...
- .Net Standard和各平台关系
.NET Standard 1.0 1.1 1.2 1.3 1.4 1.5 1.6 2.0 .NET 核心 1.0 1.0 1.0 1.0 1.0 1 ...
- 【agc014d】Black and White Tree
又是被虐的一天呢~(AC是不可能的,这辈子不可能AC的.做题又不会做,就是打打暴力,才能维持骗骗分这样子.在机房里的感觉比回家的感觉好多了!里面个个都是大佬,个个都是死宅,我超喜欢在里面的!) (↑以 ...