一致性hash算法总结

       一致性hash算法通常在分布式系统中运用比较普遍,例如多节点的数据存储,dubbo框架中也实现了一致性hash算法来选择服务提供者。还有在mysql集群,分库分表时,一致性hash也是比较经常使用的用来划分数据区域的算法。java

       举个例子,电商公司的订单表,通常都会用数据库集群或者分库分表,用多个数据库实例在承载订单信息,这么大数据量的信息,在查询的时候,首先必需要保证幂等性,就是屡次调用hash算法得出的hash值都必须同样,这样才能保证在去查询订单信息的时候,都会到对应的数据库实例中去查询,不然就可能会查不到订单信息,以前经历的就是将订单表拆分为了512张表,按照订单时间来经行hash一致性算法node

一致性hash的数据结构:mysql

   

蓝色表示机器节点,橘黄色表示数据节点算法

真实节点:A B C Dsql

虚拟节点:A1 A2 B1 B2 C1 C2 D1 D2数据库

数据存放规则:计算出各个虚拟节点的hash值(不必定非要hash算法,能够是符合本身业务逻辑的算法,只要保证有效的数据经过hash算法可以在hash环内都有落点而且具备幂等性),例如:K1的hash值∈(A1,C2),则得出K1应该存放在C2节点数据结构

本章用java实现了两种hash一致性算法;框架

1.没有虚拟节点的hash环(逻辑环)分布式

2.带虚拟节点的hash环(逻辑环)测试

备注:我这里为了方便测试,直接给对应的真是节点的hash值写死了

实现一:

package com.dubbo.algorithm;

import java.util.SortedMap;
import java.util.TreeMap;

public class ShardHash {
	
	//真实机器节点 <hash值,节点信息>
	private TreeMap<Long,String> realNodes = new TreeMap<Long,String>();
	
	public ShardHash(){
		init();
	}
	
	public void init(){
		// A B C D 4个真实节点
		realNodes.put(1L, "A");
		realNodes.put(6L, "B");
		realNodes.put(14L, "C");
		realNodes.put(25L, "D");
		/*
		realNodes.put(hash("A"), "A");
		realNodes.put(hash("B"), "B");
		realNodes.put(hash("C"), "C");
		realNodes.put(hash("D"), "D");
		*/
	}
	
	public String getNode(String key){
		//方便测试
		Long keyHash = Long.valueOf(key);
//		Long keyHash = hash(key);
		//根据key的hash值,找到hash环上面比此hash值大的节点
		SortedMap<Long,String> sorted = realNodes.tailMap(keyHash);
		if(sorted.isEmpty()){
			//没有比key的hash大的节点,则该key存放在第一个节点上面
			String node = realNodes.get(realNodes.firstKey());
			return node;
		}
		String node = sorted.get(sorted.firstKey());
		return node;
	}
	
	/** 
	 * 网上copy的一个hash算法,能够选择适合本身场景的hash算法
     */  
    private Long hash(String str) {  
        final int p = 16777619;  
        Long hash =  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) {
    	ShardHash s = new ShardHash();
    	String node = s.getNode("33");
    	System.out.println("数据存放节点为:"+node);
	}
}

实现二:

package com.dubbo.algorithm;

import java.util.LinkedList;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;

public class ShardHashV {
	//真实节点,4个真实节点
	private List<String> realNodes = new LinkedList<String>();
	//虚拟节点
	private TreeMap<Long,String> vNode = new TreeMap<Long,String>();
	//每一个真实节点对应多少个虚拟节点
	private int VNODE_NUM = 4;
	//区间大小
	private static Long count = 0L;
	
	public ShardHashV(){
		init();
	}
	
	//0--5--10--15....
	public void init(){
		realNodes.add("A1");
		realNodes.add("B1");
		realNodes.add("C1");
		realNodes.add("D1");
		for(int i=0;i<realNodes.size();i++){
			for(int j=0;j<VNODE_NUM;j++){
				vNode.put(hash(), "A"+(j+1));
				vNode.put(hash(), "B"+(j+1)); 
				vNode.put(hash(), "C"+(j+1)); 
				vNode.put(hash(), "D"+(j+1)); 
			}
		}
	}
	
	public String getNodes(String key){
		//找到全部比key大的节点
		SortedMap<Long,String> sm = vNode.tailMap(Long.valueOf(key));
		if(sm.isEmpty()){
			String node =vNode.get(vNode.firstKey()) ;
			return node;
		}
		String node =vNode.get(sm.firstKey()) ;
		return node;
	}
	
	public void remove(String key){
		vNode.remove(Long.valueOf(key));
	}
	
    public static void main(String[] args) {
    	ShardHashV sv = new ShardHashV();
    	System.out.println(sv.getNodes("0"));
    	System.out.println(sv.getNodes("7"));
    	System.out.println(sv.getNodes("11"));
    	System.out.println(sv.getNodes("16"));
    	System.out.println(sv.getNodes("54"));
    	//删除B1节点
    	sv.remove("10");
    	System.out.println(sv.getNodes("7"));
    	sv.remove("5");
    	System.out.println(sv.getNodes("3"));
	}
	/** 
	 * 网上copy的一个hash算法,能够选择适合本身场景的hash算法
     */  
    private Long hash() {  
    	Long key = this.count+5L;
    	count=count+5L;
        return key; 
    }  

}
相关文章
相关标签/搜索