一致哈希算法(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 nodes;

public ConsistentHash() {

nodes = new ArrayList();

}

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 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 datas) {

StringBuffer buffer = new StringBuffer();

buffer.append("{");

Iterator 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 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 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 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 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 datas;

public Node(String name) {

this.name = name;

datas = new LinkedList();

}

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 getDatas() {

return datas;

}

public void setDatas(LinkedList 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实现相关推荐

  1. java 网页正文抽取算法_网页正文抽取算法 ContentExtractor

    软件介绍 简介 ContentExtractor 是一个开源的网页正文抽取工具,用JAVA实现,具有非常高的抽取精度. 算法 ContentExtractor的网页正文抽取算法使用的是CEPR,适用于 ...

  2. java实现子序列最大和_“最大子序列和”算法 java

    maxSubSum各自是最大子序列和的4中java算法实现. 第一种算法执行时间为O(N^3),另外一种算法执行时间为O(N^2),第三种算法执行时间为O(nlogn),第四种算法执行时间为线性N p ...

  3. C++ 哈希表查询_进入哈希函数结界的世界

    1. 前言 哈希表或称为散列表,是一种常见的.使用频率非常高的数据存储方案. 哈希表属于抽象数据结构,需要开发者按哈希表数据结构的存储要求进行 API 定制,对于大部分高级语言而言,都会提供已经实现好 ...

  4. java webservice报文过长_工作1-5年的Java程序猿到底需要怎样的一个技术栈?

    工作1-5年的Java程序猿到底需要怎样的一个技术栈? 前言: 具有1-5年开发经验的程序员 需要学习的内容其实还有很多很多. 今天跟大家交流一下希望分享出来的对大家能够有帮助,这是我这些年总结出的一 ...

  5. hash算法_数据库中间件分片算法之hash

    前言 夜深人静的时候,打开云音乐,点上一曲攀登,带上真无线蓝牙耳机,瞬间燃到爆,键盘打字如飞倦意全无. 分片规则 这几天有人问我,dble和MyCat到底有什么不同.其实dble作为MyCAT的同门, ...

  6. java实现次方的运算_【技术干货】Java 面试宝典:Java 基础部分(1)

    海牛学院的 | 第 616 期 本文预计阅读 |18 分钟 Java 基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语法,集合的语法,io 的语法, ...

  7. mysql区间算法_「五大常用算法」一文图解分治算法和思想

    前言 分治算法(divide and conquer)是五大常用算法(分治算法.动态规划算法.贪心算法.回溯法.分治界限法)之一,很多人在平时学习中可能只是知道分治算法,但是可能并没有系统的学习分治算 ...

  8. java中execution的作用_一文初步了解Java虚拟机

    大家都知道,Java中JVM的重要性,学习了JVM你对Java的运行机制.编译过程和如何对Java程序进行调优相信都会有一个很好的认知. 什么是JVM? JVM(Java Virtual Machin ...

  9. java语言用什么编程_使用什么编程语言开发Java?

    The Java Language is a language specification – you don't write a language in a language, it's a spe ...

最新文章

  1. 模式6--ReadWriteLock
  2. JAVA代码实现多级树结构封装对象
  3. python编程规范
  4. php5.3二进制包,php使用pack处理二进制文件的方法
  5. 转:如何提高自己的归纳总结能力?
  6. PyCharm安装好vim后,怎么配置.vimrc
  7. MySQL 导出数据
  8. Leaflet文档阅读笔记-Leaflet on Mobile笔记
  9. Linux btrfs之文件系统转换
  10. 可视化排班管理_呼叫中心外包之管理要点与数据分析对策
  11. iOS开发系列-ARC浅解
  12. VS2017无法保存工具栏布局信息
  13. 软激活WIN7 Activation
  14. Web前端-课程设计-网易严选
  15. Html post易语言服务器,POST其实很简单 15 易语言模块之精易模块
  16. Lionel Messi
  17. 电脑主板资料库 07【转至www.ongood.com.tw】【FreeXploiT收集整理】
  18. win7查找计算机在哪里,win7搜索在哪
  19. out of synch 不同步是什么意思?
  20. 在github上下载的.md文件怎么打开

热门文章

  1. jvm回收垃圾_没有垃圾回收的JVM
  2. spock测试_使用Spock测试您的代码
  3. java解码_Java数组已排序解码
  4. 多个定时器相互干扰的问题_相互问题
  5. 哈希策略_优化哈希策略的简介
  6. jax-rs jax-ws_在JAX-RS中使用@Context [第1部分]
  7. jpa 返回数据转换_如何使用JPA类型转换器加密数据
  8. 如何用Java编写类似C的Sizeof函数
  9. 向Java最佳专家的全球专家学习Java
  10. JDeps入门–分析项目的依赖关系