一致性哈希理解

63 阅读2分钟

java中hashcode

有时候我们新写个类的话,我们要重写类的hashcode,equals方法

  • 因为对象比较会先比较hashcode是否相等,再去比较equals方法
  • 在HashMap中的数组+链表的结构是通过key的hash存放的
    public class HashTest {
        	private String key;
        	private String value;
        	
        	@Override
        	public int hashCode() {
        		return Objects.hash(key, value);
        	}
        	@Override
        	public boolean equals(Object obj) {
        		if (this == obj)
        			return true;
        		if (obj == null)
        			return false;
        		if (getClass() != obj.getClass())
        			return false;
        		HashTest other = (HashTest) obj;
        		return Objects.equals(key, other.key) && Objects.equals(value, other.value);
        	}     	
        }

哈希

  • 做负载的时候,我们想让某个ip的访问一直请求在某一个台服务器中
  • 做分库分表的时候我们想把数据分布在不同的DB上
  • 还有个就是做缓存大key的时候,拆分数据

问题

  • 扩容的问题需要重新计算hash
  • 最常见的做法 hash(key)%n 但这有个问题就是key的分布不均

一致性哈希(geektutu.com/post/geecac…)

image.png

结构流程

  • 一致性哈希算法将 key 映射到 2^32 的空间中,将这个数字首尾相连,形成一个环。
  • 图1中有peer2,4,6三个节点,节点查找是算出来的hash最近一个>=它的值
  • key11,key2属于peer2节点,key23属于peer4节点,key27属于peer2节点
  • 图2扩容添加了peer8节点那么需要修改的只有key27节点,就是>=上一个节点和<=当前节点的数据

分布不均的问题

  • 这里引入了虚拟节点,也就是一个机器对应多个节点
  • 当查找key对应的节点的时候,先求出hash找到最近一个>=的值,然后再找节点

java的demo

public class Consistenthash {

private int keys[];//槽位

private int replace;//虚拟个数

private Map<Integer, String> mappingHash;

public Consistenthash() {

keys=new int[10000];//槽位限制住了,实际是2^32 可以用其他集合代替

replace=5;

mappingHash=new HashMap<>();

}

//添加节点

public void addMachine(String des) {

for(int i=0;i<replace;i++) {

int hash=oneByOneHash(des+i)%keys.length;

keys[hash]=1;

mappingHash.put(hash, des);

System.out.println("[机器]:"+des+"[槽位]:"+hash);
}

}

//通过key获取节点

public String getDescBykey(String key) {

String descString="";
int hash=oneByOneHash(key)%keys.length;
int index=-1;
for(int i=hash;i<keys.length;i++) {
if(keys[i]>0) {
index=i;
break;
}
}

if(index>-1) {
descString=mappingHash.get(Integer.valueOf(index));
}

System.out.println("[key]:"+key+"[hash]:"+hash+"[在节点]:"+descString);
return descString;
}

/**

* 一次一个hash

* @param key

* @return 输出hash值

*/

public int oneByOneHash(String key) {

int hash, i;

for (hash = 0, i = 0; i < key.length(); ++i) {

hash += key.charAt(i);

hash += (hash << 10);

hash ^= (hash >> 6);

}

hash += (hash << 3);

hash ^= (hash >> 11);

hash += (hash << 15);

// return (hash & M_MASK);

if(hash<0) {

return Math.abs(hash);

}

return hash;

}

public static void main(String[] args) {

Consistenthash consistenthash=new Consistenthash();

consistenthash.addMachine("机器1");

consistenthash.addMachine("机器2");

consistenthash.addMachine("机器3");

consistenthash.getDescBykey("student");

consistenthash.getDescBykey("sql");

}

}

效果

image.png