第五周 作业 1
发布于: 2020 年 10 月 25 日
用你熟悉的编程语言实现一致性 hash 算法。
ConsistentHash.java
public class ConsistentHash {
private static ConcurrentSkipListMap<Integer, String> nodes = new ConcurrentSkipListMap();
// 每台机器虚拟节点数默认为 150
private int virtualNumPerNode = 150;
public ConsistentHash(List<String> nodes, int virtualNumPerNode) {
this.virtualNumPerNode = virtualNumPerNode;
for (String node:nodes) {
addVirtualNodes(node);
}
}
private void addVirtualNodes(String node) {
for (int i=0; i<this.virtualNumPerNode; i++) {
// 虚拟节点使用 192.168.1.1#1 表示虚拟的第几个节点
String virtualNode = node+"#"+i;
nodes.put(getHash(virtualNode), virtualNode);
}
}
// 根据 key 找到存储数据的真实机器节点
public String getNode(Object key) {
Integer hashCode = getHash(key);
Map.Entry<Integer, String> entry = nodes.ceilingEntry(hashCode);
if (entry == null) {
entry = nodes.ceilingEntry(0);
}
String virtualNode = entry.getValue();
return virtualNode.substring(0, virtualNode.indexOf("#"));
}
// 使用FNV1_32_HASH算法计算服务器的Hash值,hash值能大于 2^31
private int getHash(Object object) {
final int p = 16777619;
int hash = (int) 2166136261L;
String string = object.toString();
for (int i = 0; i < string.length(); i++)
hash = (hash ^ string.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;
//return Math.abs(string.hashCode());
}
}
复制代码
编写测试用例测试这个算法,测试 100w kv 数据,10 个服务器节点的情况下,计算这些 KV 数据在服务器上分布数量的标准差,以评估算法的存储负载不均衡性。
MathUtils.java
public class MathUtils {
// 求平均数
public static Double averageValue(Integer[] data) {
Double sum = 0.0;
for (Integer d:data) {
sum += d;
}
return sum/data.length;
}
// 求标准差
public static Double standardVariance(Integer[] data) {
Double avg = MathUtils.averageValue(data);
Double var = 0.0;
for (Integer d:data) {
// 当前数与平均数的差的平方
var += ((d-avg)*(d-avg));
}
return Math.sqrt((var/data.length));
}
}
复制代码
ConsistentHashTest.java
public class ConsistentHashTest {
@Test
public void getNode() {
List<String> nodes = Arrays.asList(
"192.168.0.1",
"192.168.0.2",
"192.168.0.3",
"192.168.0.4",
"192.168.0.5",
"192.168.0.6",
"192.168.0.7",
"192.168.0.8",
"192.168.0.9",
"192.168.0.10"
);
ConsistentHash consistentHash = new ConsistentHash(nodes, 250);
Map<String, Integer> stat = new HashMap<>();
for (int i=0; i<1000000; i++) {
String randomString = generateRandomString();
String node = consistentHash.getNode(randomString);
stat.put(node, stat.getOrDefault(node, 0)+1);
}
for (String node:nodes) {
System.out.println("node: "+node+"; count: "+ stat.get(node));
}
Integer[] data = stat.values().toArray(new Integer[0]);
Double standardVariance = MathUtils.standardVariance(data);
System.out.println("sdv => "+standardVariance);
}
private String generateRandomString() {
return UUID.randomUUID().toString();
}
}
复制代码
将虚拟节点设置为不同值得到的标准差见下表
得出逻辑不完全精确的结论是:当节点数很少时每个节点的存放的 key 数稳定,随着节点增多,每个机器的存放数量波动越小,当节点大于 150 后,随着节点数增多,波动减小的效果逐渐变差。
划线
评论
复制
发布于: 2020 年 10 月 25 日阅读数: 47
版权声明: 本文为 InfoQ 作者【Yangjing】的原创文章。
原文链接:【http://xie.infoq.cn/article/e05fcbf585770b8ca84df59ec】。
本文遵守【CC BY-NC】协议,转载请保留原文出处及本版权声明。
Yangjing
关注
还未添加个人签名 2017.11.09 加入
还未添加个人简介
评论