一致性hash算法测试

标签: 一致性 hash 算法 | 发表时间:2014-12-17 18:40 | 作者:xiaoliang330
出处:http://www.iteye.com
因为用memcached集群缓存数据,所以增删服务器节点 对缓存key的影响需要考虑一种策略来实现 数据缓存key所映射的节点变动至最小值(这句话好长啊,就是缓存服务器的增减,对在已经缓存了的数据影响降到最小,比如“test”这个数据之前存在a1节点服务器上,那么增加删除了服务器节点,‘test’依然在 a1上(有可能不在,这个原因可以看以下代码),用10个数据来说明吧,感觉有点只可意会不可言传,10个数据,在节点变化时,尽量只有2个数据发生变动,ok)


下面代码示例:




package com.xll;
//服务器对象
public class Server {

	private String name;
	private String password;
	private String ip;
	private String port;

	public Server() {
	}

	public Server(String name, String password, String ip, String port) {
		super();
		this.name = name;
		this.password = password;
		this.ip = ip;
		this.port = port;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getIp() {
		return ip;
	}

	public void setIp(String ip) {
		this.ip = ip;
	}

	public String getPort() {
		return port;
	}

	public void setPort(String port) {
		this.port = port;
	}

	@Override
	public String toString() {
		return "name:" + name + "ip:" + ip + "port:" + port;
	}

}







package com.xll;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
//server注册,算法生成
public class ServerLoadBalance {

	private TreeMap<Long, Server> nodes;

	private List<Server> servers;

	private final int NODE_NUM = 200;

	public ServerLoadBalance(List<Server> servers) {
		super();
		this.servers = servers;
		init();
	}
	
	private void init() { // 初始化一致性hash环
		nodes = new TreeMap<Long, Server>();
		for (int i = 0; i != servers.size(); ++i) { // 每个真实机器节点都需要关联虚拟节点
			final Server shardInfo = servers.get(i);

			for (int n = 0; n < NODE_NUM; n++){
				// 一个真实机器节点关联NODE_NUM个虚拟节点
				Long hash_value = hash("SHARD-" + shardInfo.getIp() + "-NODE-" + n);
				//System.out.println("第"+i+"个server的hash 散列值="+hash_value);
				nodes.put(hash_value, shardInfo);
			}
				

		}
		System.out.println("Finish inited virtual node....");
	}

	/**
	 * 通过key的一致性hash值 得到 相应的 Server对象
	 * @param key
	 * @return
	 */
	public Server getShardInfo(String key) {
		Long hash_value = hash(key);
		//System.out.println("key="+key+"的hash值="+hash_value);
		
		SortedMap<Long, Server> tail = nodes.tailMap(hash_value); // 沿环的顺时针找到一个虚拟节点
		if (tail.size() == 0) {
			return nodes.get(nodes.firstKey());
		}
		return tail.get(tail.firstKey()); // 返回该虚拟节点对应的真实机器节点的信息
	}

	/**
	 * 一致性hash算法
	 * @param key
	 * @return
	 */
	private Long hash(String key) {

		ByteBuffer buf = ByteBuffer.wrap(key.getBytes());
		int seed = 0x1234ABCD;

		ByteOrder byteOrder = buf.order();
		buf.order(ByteOrder.LITTLE_ENDIAN);

		long m = 0xc6a4a7935bd1e995L;
		int r = 47;

		long h = seed ^ (buf.remaining() * m);

		long k;
		while (buf.remaining() >= 8) {
			k = buf.getLong();

			k *= m;
			k ^= k >>> r;
			k *= m;

			h ^= k;
			h *= m;
		}

		if (buf.remaining() > 0) {
			ByteBuffer finish = ByteBuffer.allocate(8).order(
					ByteOrder.LITTLE_ENDIAN);
			// for big-endian version, do this first:
			// finish.position(8-buf.remaining());
			finish.put(buf).rewind();
			h ^= finish.getLong();
			h *= m;
		}

		h ^= h >>> r;
		h *= m;
		h ^= h >>> r;

		buf.order(byteOrder);
		return h;
	}
}







package com.xll;

import java.util.ArrayList;
import java.util.List;
//主要的测试类
public class TestConsistenHash {
	
	public static Server s1 = new Server("ser1", "123", "192.168.216.1", "1");
	public static Server s2 = new Server("ser2", "123", "192.168.216.2", "2");
	public static Server s3 = new Server("ser3", "123", "192.168.216.3", "3");
	public static Server s4 = new Server("ser4", "123", "192.168.216.4", "4");
	public static Server s5 = new Server("ser5", "123", "192.168.216.5", "5");
	public static List<Server> list = new ArrayList<Server>();
	
	public static int count1 = 0;
	public static int count2 = 0;
	public static int count3 = 0;
	public static int count4 = 0;
	public static int count5 = 0;
	
	
	public static void main(String[] args) {
		
		
		
		/**
		 * 可以通过  增加 / 剔除 server来测试 被点击量 的变化
		 */
		list.add(s1);
		list.add(s2);
		list.add(s3);
		list.add(s4);
		list.add(s5);
		
		
		
		
		/**
		 * key的数量为1000个
		 * 
		 * 
		 * 
		 * 在ServerLoadBalance 的 virtual node 数量NODE_NUM 为200时(这个值能达到负载均衡了,100或更小时还没达到)
		 * 测试的两组数据(5台server和剔除server3时的4台server)
		 * 
		 * 
		 * server1 hit count = 197
		 * server2 hit count = 193
		 * server3 hit count = 210
		 * server4 hit count = 170
		 * server5 hit count = 230
		 * 
		 * 
		 * server1 hit count = 265
		 * server2 hit count = 248
		 * server3 hit count = 0
		 * server4 hit count = 214
		 * server5 hit count = 273
		 * 
		 * 
		 * 
		 * 
		 * 在ServerLoadBalance 的 virtual node 数量NODE_NUM 为1时(负载均衡在100或更小时还没达到)
		 * 测试的两组数据(5台server和剔除server3时的4台server)
		 * server1 hit count = 359
		 * server2 hit count = 94
		 * server3 hit count = 387
		 * server4 hit count = 67
		 * server5 hit count = 93
		 * 
		 * 
		 * server1 hit count = 359
		 * server2 hit count = 481
		 * server3 hit count = 0
		 * server4 hit count = 67
		 * server5 hit count = 93
		 * 
		 * 
		 * 
		 * 
		 */
		
		
		
		
		ServerLoadBalance server = new ServerLoadBalance(list);
		

		for (int i = 0; i < 1000; i++) {
			String key =  ""+(i+20);
			
			Server s = (Server)server.getShardInfo(key);
			if(s.toString().equals(s1.toString()))
				count1 ++;
			else if(s.toString().equals(s2.toString()))
				count2 ++;
			else if(s.toString().equals(s3.toString()))
				count3 ++;
			else if(s.toString().equals(s4.toString()))
				count4 ++;
			else
				count5 ++;
			
			
			//System.out.println("key" + i + ", server=" + s);
			
		}
		
		
		//得到各server的命中 情况
		System.out.println("#############");
		System.out.println("server1 hit count = "+TestConsistenHash.count1);
		System.out.println("server2 hit count = "+TestConsistenHash.count2);
		System.out.println("server3 hit count = "+TestConsistenHash.count3);
		System.out.println("server4 hit count = "+TestConsistenHash.count4);
		System.out.println("server5 hit count = "+TestConsistenHash.count5);
		System.out.println("#############");
		
		
		
//		String key = "test";
//		TestThread tt = new TestThread(shard, key);
//		
//		new Thread(tt, "0").start();
//		new Thread(tt, "10001").start();
//		new Thread(tt, "20001").start();
//		new Thread(tt, "30001").start();
//		new Thread(tt, "40001").start();
//		new Thread(tt, "50001").start();

		
		
		
		
	}

}







package com.xll;
//用于跑大量数据
public class TestThread extends Thread{
	ServerLoadBalance shard;
	String key;
	
	public TestThread(ServerLoadBalance shard,String key){
		this.shard = shard;
		this.key = key;
	}
	
	
	@Override
	public void run() {
		runHash();
		printCountValue();
	}
	
	public  void runHash(){
		
		String name = currentThread().getName();
		int start = Integer.parseInt(name);
		
			for (int i = start; i < start+10000; i++) {
				Server s = (Server)shard.getShardInfo(key+i);
				increaseCount(s);
			}
		
	}
	
	public void increaseCount(Server s){
		if(s.toString().equals(TestConsistenHash.s1.toString()))
			increase(1);
		else if(s.toString().equals(TestConsistenHash.s2.toString()))
			increase(2);
		else if(s.toString().equals(TestConsistenHash.s3.toString()))
			increase(3);
		else if(s.toString().equals(TestConsistenHash.s4.toString()))
			increase(4);
		else
			increase(5);
	}
	
	static synchronized void increase(int i){
		
		switch (i) {
		case 1:
			TestConsistenHash.count1 ++;
			break;
		case 2:
			TestConsistenHash.count2 ++;
			break;
		case 3:
			TestConsistenHash.count3 ++;
			break;
		case 4:
			TestConsistenHash.count4 ++;
			break;
		default:
			TestConsistenHash.count5 ++;
			break;
		}
		
	}

	
	void printCountValue(){
		System.out.println("#############");
		System.out.println("server1 hit count = "+TestConsistenHash.count1);
		System.out.println("server2 hit count = "+TestConsistenHash.count2);
		System.out.println("server3 hit count = "+TestConsistenHash.count3);
		System.out.println("server4 hit count = "+TestConsistenHash.count4);
		System.out.println("server5 hit count = "+TestConsistenHash.count5);
		System.out.println("#############");
		
	}
}







在测试类中,有几组数据对一致性的测试,当每个真实的服务器节点只有一个虚拟节点时,去掉一台机器,那么这台机器的负荷只会倾倒性的转移到一台服务器上,其他节点数据和负荷不发生变化, 这个验证了: 一致性hash在某个节点变化时,其他节点上的原来的数据依然保持


当每个节点的虚拟节点数增加至200个时, 这时去掉一台服务器节点,这个节点的访问量变为0, 其负载均衡的分摊至每个还存在的服务器节点上,这个验证了: 一致性hash还能达到负载均衡,而不会导致某台负荷过重而导致 多米诺(相继挂掉)的效应






已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [一致性 hash 算法] 推荐:

一致性HASH算法

- - 企业架构 - ITeye博客
一致性 hash 算法( consistent hashing ). consistent hashing 算法早在 1997 年就在论文 . Consistent hashing and random trees 中被提出,目前在cache 系统中应用越来越广泛;. 比如你有 N 个 cache 服务器(后面简称 cache ),那么如何将一个对象 object 映射到 N 个 cache 上呢,你很可能会采用类似下面的通用方法计算 object 的 hash 值,然后均匀的映射到到 N 个 cache ;.

一致性Hash算法背景(转)

- - 开源软件 - ITeye博客
一致性哈希算法在1997年由麻省理工学院的Karger等人在解决分布式Cache中提出的,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似. 一致性哈希修正了CARP使用的简单哈希算法带来的问题,使得DHT可以在P2P环境中真正得到应用.   但现在一致性hash算法在分布式系统中也得到了广泛应用,研究过memcached缓存数据库的人都知道,memcached服务器端本身不提供分布式cache的一致性,而是由客户端来提供,具体在计算一致性hash时采用如下步骤:.

一致性hash算法测试

- - Java - 编程语言 - ITeye博客
package com.xll; //服务器对象 public class Server {. private void init() { // 初始化一致性hash环. for (int i = 0; i != servers.size(); ++i) { // 每个真实机器节点都需要关联虚拟节点.

一致性hash

- - 互联网 - ITeye博客
一致性hash算法 - consistent hashing. 分类:  算法艺术2010-02-02 09:19 69836人阅读  评论(97)  收藏  举报. 算法 cache object 服务器 存储 c. 一致性 hash 算法( consistent hashing ).

[转][转]memcache的一致性hash算法使用

- - heiyeluren的Blog
来源: http://blog.csdn.net/kongqz/article/details/6695417.   1、我们的memcache客户端(这里我看的spymemcache的源码),使用了一致性hash算法ketama进行数据存储节点的选择. 与常规的hash算法思路不同,只是对我们要存储数据的key进行hash计算,分配到不同节点存储.

一致性hash算法在memcached中的使用

- - CSDN博客推荐文章
  1、我们的memcache客户端(这里我看的spymemcache的源码),使用了一致性hash算法ketama进行数据存储节点的选择. 与常规的hash算法思路不同,只是对我们要存储数据的key进行hash计算,分配到不同节点存储. 一致性hash算法是对我们要存储数据的服务器进行hash计算,进而确认每个key的存储位置.

【转】一致性hash算法与server列表维护

- - 编程语言 - ITeye博客
考虑到不用重复造轮子,特此转载好文,出处http://shift-alt-ctrl.iteye.com/blog/1963244.     普通的hash算法有个很大的问题:当hash的"模数"发生变化时,整个hash数据结构就需要重新hash,重新hash之后的数据分布一定会和hash之前的不同;在很多场景下,"模数"的变化时必然的,但是这种"数据分布"的巨大变化却会带来一些麻烦.所以,就有了"一致性hash",当然学术界对"一致性hash"的阐述,还远远不止这些..

Hash算法的使用

- khsing - Glider&#39;s home
在对语料文本进行2,3元切分时,需要借助hash表来获得切分内容在内存中的位置,以便能够记录语料库中出现的次数. 以前知道有很多hash算法,但没认真研究过,今天才知道hash算法差距还是很明显的. 首先我选择的是暴雪在魔兽里的hash算法,这个算法很高级,是time33类型的一个变种(有关time33的介绍,可以参考:http://www.cnblogs.com/napoleon_liu/articles/1911571.html),而且很巧妙的借助3次hash避免字符串比较这种费时的操作,并且不用链表来存储冲突,3次hash冲突值能相等的可能行只有1/10的23次方,应该说不可能冲突了.

memcached的总结和分布式一致性hash

- - 开源软件 - ITeye博客
当前很多大型的web系统为了减轻数据库服务器负载,会采用memchached作为缓存系统以提高响应速度. memcached是一个开源的高性能分布式内存对象缓存系统. 其实思想还是比较简单的,实现包括server端(memcached开源项目一般只单指server端)和client端两部分:. server端本质是一个in-memory key-value store,通过在内存中维护一个大的hashmap用来存储小块的任意数据,对外通过统一的简单接口(memcached protocol)来提供操作.

从头到尾彻底解析Hash表算法

- - 博客园_知识库
  作者:July、wuliming、pkuoliver.   说明:本文分为三部分内容, 第一部分为一道百度面试题Top K算法的详解;第二部分为关于Hash表算法的详细阐述;第三部分为打造一个最快的Hash表算法.    第一部分:Top K 算法详解.   问题描述(百度面试题):.   搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节.