前面提到分布式数据库数据分区规则有
【节点取余分区】
【一致性哈希分区】
【虚拟hash槽分区】
一致性HASH原理:在处理负载策略时:
- 一个hash环,从0到正整数
- 四个服务器ip计算的hash值肯定会落到这个hash环上的某一个点
- 根据hash(用户id)计算路由规则(hash值),然后看hash值落到了hash环的那个地方,根据hash值在hash环上的位置顺时针找距离最近的ip作为路由ip
数据分区也是同理。
如图:
- user1的请求会落到服务器ip1进行处理
- user2,user3的请求会落到服务器ip3进行处理
- user4的请求会落到服务器ip4进行处理
public class IdenticalHash {
//服务器列表
public static String[] serverIps = {"192.168.0.1",
"88.89.90.91", "77.78.79.80", "127.0.0.1"};
//key表示服务器的hash值,value表示服务器
public static TreeMap<Integer, String> hashRing = new TreeMap<Integer, String>();
//通过用户ip得到处理该ip的服务器地址
public static String getServerByIdHash(String userId) {
int hash = hash(userId);
//得到大于该Hash值的所有Map
SortedMap<Integer, String> moreThanIpMap = hashRing.tailMap(hash);
if (moreThanIpMap.isEmpty()) {
//如果没有比该key的hash值大的,则从第一个node开始
return hashRing.get(hashRing.firstKey());
}
//第一个Key就是顺时针过去离node最近的那个结点
return moreThanIpMap.get(moreThanIpMap.firstKey());
}
//根据key获取hash值(复用hashmap中的hash算法)
public static int hash(String str) {
int h;
return (str == null) ? 0 : (h = str.hashCode()) ^ (h >>> 16);
}
//将服务器ip放到hash环中
public static void putHashRing() {
for (int i = 0; i < serverIps.length; i++) {
int hash = hash(serverIps[i]);
hashRing.put(hash, serverIps[i]);
System.err.println(serverIps[i] + " 在hash环中的位置:" + hash);
}
}
public static void main(String[] args) {
putHashRing();
String[] userIds = {"1241414", "42452356", "4214", "777789"};
for (int i = 0; i < userIds.length; i++) {
System.out.println("用户id:" + userIds[i] + " 的hash值为" + hash(userIds[i])
+ ", 交由服务器:" + getServerByIdHash(userIds[i]) + " 处理");
}
}
}
上诉hash环中的服务器ip分配不均。也可能会导致某服务器处理大量的请求,有服务器会闲置
均匀一致性hash
均匀一致性hash的目标是如果服务器有N台,客户端的hash值有M个,那么每个服务器应该处理大概M/N个用户的。也就是每台服务器负载尽量均衡.