一致性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; 
    }  

}
相關文章
相關標籤/搜索