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

}

须要进一步完好的地方

不同节点之间互相备份,提高系统的可靠性。节点范围的动态调整。有时候分布可能不够平衡。

时间: 2024-11-07 04:57:47

一致哈希算法Java实现的相关文章

感知哈希算法的java实现

一.原理讲解      实现这种功能的关键技术叫做"感知哈希算法"(Perceptual Hash Algorithm), 意思是为图片生成一个指纹(字符串格式), 两张图片的指纹越相似, 说明两张图片就越相似. 但关键是如何根据图片计算出"指纹"呢? 下面用最简单的步骤来说明一下原理:            <1>.第一步 缩小图片尺寸      将图片缩小到8x8的尺寸, 总共64个像素. 这一步的作用是去除各种图片尺寸和图片比例的差异, 只保留结构

Java进阶(五十七)-基于感知哈希算法的图像配准

Java进阶(五十七)-基于感知哈希算法的pHash图像配准算法 ??毕业论文提交之后,老师交给自己一项任务:图像配准,也就是给你两幅图像,通过系统来判定两幅图像是否为同一副图像.自己作为这一方面的小白,先去网上搜索一下相应的检测方法,当然有现成的API调用最好,花钱也无所谓. ??我们这里采用的基础关键技术叫做 "感知哈希算法"(Perceptual hash algorithm),它的作用是对每张图片生成一个"指纹"(fingerprint)字符串,然后比较不同

一致性哈希算法与Java实现

一致性哈希算法是分布式系统中常用的算法.比如,一个分布式的存储系统,要将数据存储到具体的节点上,如果采用普通的hash方法,将数据映射到具体的节 点上,如key%N,key是数据的key,N是机器节点数,如果有一个机器加入或退出这个集群,则所有的数据映射都无效了,如果是持久化存储则要做数据 迁移,如果是分布式缓存,则其他缓存就失效了. 因此,引入了一致性哈希算法: 把数据用hash函数(如MD5),映射到一个很大的空间里,如图所示.数据的存储时,先得到一个hash值,对应到这个环中的每个位置,如

一致性哈希算法(consistent hashing)(转)

原文链接:每天进步一点点——五分钟理解一致性哈希算法(consistent hashing) 一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似.一致性哈希修正了CARP使用的简 单哈希算法带来的问题,使得分布式哈希(DHT)可以在P2P环境中真正得到应用. 一致性hash算法提出了在动态变化的Cache环境中,判定哈希算法好坏的四个定义: 1.平衡性(Balance):平衡性是指哈希的

Simhash相似哈希算法

前言 最近在阅读吴军博士的<<数学之美>>这门书,得到了很多的启发和思考,里面提到了一个概念---信息指纹.一般正常人提到这个概念,第一个想到的词应该是哈希映射算法,将任何对象都映射成一个独立的变量,一般这个变量是一个独有的数字,当然也不排除哈希碰撞的可能行.论单个对象,用哈希算法做一次映射,比较对象是否一致,这固然是可以的,但是如果想用哈希算法做一些文章之间的相似度计算的时候,可能传统的哈希算法就不见得是最佳的选择了,如果把整篇文章都作为一个超长字符串的去计算,准确率无法保证,因

一致性哈希与java实现

一致性哈希算法是分布式系统中常用的算法.比如,一个分布式的存储系统,要将数据存储到具体的节点上,如果采用普通的hash方法,将数据映射到具体的节点上,如key%N,key是数据的key,N是机器节点数,如果有一个机器加入或退出这个集群,则所有的数据映射都无效了,如果是持久化存储则要做数据迁移,如果是分布式缓存,则其他缓存就失效了. 因此,引入了一致性哈希算法: 把数据用hash函数(如MD5),映射到一个很大的空间里,如图所示.数据的存储时,先得到一个hash值,对应到这个环中的每个位置,如k1

一致性哈希算法的应用及实现

一致性哈希算法(Consistent Hashing Algorithm)是一种分布式算法,由MIT的Karger及其合作者提出,现在这一思想已经扩展到其它领域.1997年发表的学术论文中介绍了“一致性哈希”如何应用于用户易变的分布式Web服务中.一致性哈希可用于实现健壮缓存来减少大型Web应用中系统部分失效带来的负面影响.(维基百科) >>hash算法的单调性 Hash 算法的一个衡量指标是单调性( Monotonicity ),定义如下:单调性是指如果已经有一些内容通过哈希分派到了相应的缓

一致性哈希算法(consistent hashing)

memcache的一致性hash算法使用 http://blog.csdn.net/kongqz/article/details/6695417 一.概述 1.我们的memcache客户端(这里我看的spymemcache的源码),使用了一致性hash算法ketama进行数据存储节点的选择.与常规的hash算法思路不同,只是对我们要存储数据的key进行hash计算,分配到不同节点存储.一致性hash算法是对我们要存储数据的服务器进行hash计算,进而确认每个key的存储位置.  2.常规hash

哈希算法集锦

因为工作原因,看了一下redis的底层实现,发现redis底层使用的哈希算法是murmurhash,第一次听说这个算法感觉自己对哈希值计算的常用算法了解太少,整理了一下网上讲的比较原理性的观点: 简介   哈稀函数按照定义可以实现一个伪随机数生成器(PRNG),从这个角度可以得到一个公认的结论:哈希函数之间性能的比较可以通过比较其在伪随机生成方面的比较来衡量. 一些常用的分析技术,例如泊松分布可用于分析不同的哈希函数对不同的数据的碰撞率(collision rate).一般来说,对任意一类的数据