写点什么

架构师训练营第 5 周作业

用户头像
时来运转
关注
发布于: 2020 年 07 月 08 日

用你熟悉的编程语言实现一致性 hash 算法。

编写测试用例测试这个算法,测试 100 万 KV 数据,10 个服务器节点的情况下,计算这些 KV 数据在服务器上分布数量的标准差,以评估算法的存储负载不均衡性。



测试结果输出如下:

虚拟节点数 10050 的标准差 5.792715732327589

虚拟节点数 200 的标准差 6.036923425424944

虚拟节点数 9050 的标准差 6.200358412579424

虚拟节点数 250 的标准差 6.306962642808167

虚拟节点数 2050 的标准差 6.6332495807108

虚拟节点数 7050 的标准差 7.0710678118654755

虚拟节点数 400 的标准差 7.118052168020874

虚拟节点数 450 的标准差 7.195677714974301

虚拟节点数 3050 的标准差 7.211102550927978

虚拟节点数 500 的标准差 7.453559924999299

虚拟节点数 350 的标准差 7.745966692414834

虚拟节点数 8050 的标准差 7.859884081701064

虚拟节点数 300 的标准差 8.04155872120988

虚拟节点数 550 的标准差 8.246211251235321

虚拟节点数 4050 的标准差 8.628119403696523

虚拟节点数 6050 的标准差 8.781293248212995

虚拟节点数 950 的标准差 8.94427190999916

虚拟节点数 30050 的标准差 8.96908269804914

虚拟节点数 5050 的标准差 9.055385138137417

虚拟节点数 100050 的标准差 9.899494936611665

虚拟节点数 40050 的标准差 10.011104945120804

虚拟节点数 1050 的标准差 10.044346115546242

虚拟节点数 50050 的标准差 10.055402085998905

虚拟节点数 750 的标准差 10.099504938362077

虚拟节点数 60050 的标准差 10.16530045465127

虚拟节点数 850 的标准差 10.913803695829934

虚拟节点数 20050 的标准差 11.15546702045434

虚拟节点数 90050 的标准差 11.88836966675134

虚拟节点数 150 的标准差 12.54325848148452

虚拟节点数 70050 的标准差 12.631530214330944

虚拟节点数 650 的标准差 13.824294235551815

虚拟节点数 80050 的标准差 13.888444437333106

虚拟节点数 100 的标准差 15.726834816113932

虚拟节点数 50 的标准差 16.766368453279053



代码如下:

package com.jk.test.hash;



import java.util.ArrayList;

import java.util.LinkedHashMap;

import java.util.List;

import java.util.SortedMap;

import java.util.TreeMap;



public class UniformityHash {



private int nodeCount;

private int virtualNum;

List<String> serviceIpList = new ArrayList<String>();

List<String> virtualIpList = new ArrayList<String>();

TreeMap<Integer,String> serviceMap = new TreeMap<Integer,String>();

public UniformityHash(int defualNodeCount,int virtualNum , List<String> serviceIpList){

this.nodeCount = defualNodeCount;

this.virtualNum = virtualNum;

this.serviceIpList = serviceIpList;

init();

}

/**

* 初始化数据

*/

private void init()

{

if(null == serviceIpList || serviceIpList.size()==0)

{

serviceIpList = new ArrayList<String>();

String serviceIp = "192.168.0.";

for(int i=0;i<nodeCount;i++)

{

serviceIpList.add(serviceIp+(i+1));

}

}

for(String serviceIp:serviceIpList)

{

serviceMap.put(this.getHash(serviceIp), serviceIp);

for(int i=0;i<virtualNum;i++)

{

String virtualIp = serviceIp+"virtual"+i;

serviceMap.put(this.getHash(virtualIp), virtualIp);

}

}

}

/**

* 获取对应的服务器

* @param str

* @return

*/

public String getServiceIp(String str)

{

int hash = this.getHash(str);

SortedMap<Integer,String> map = serviceMap.tailMap(hash);

if(map.isEmpty())

{

return serviceMap.get(serviceMap.firstKey());

}

String virtualIp = map.get(map.firstKey());

int end = virtualIp.indexOf("virtual");

String returnStr = end> 0 ?virtualIp.substring(0, virtualIp.indexOf("virtual")):virtualIp;

if(returnStr.contains("virtual"))

{

System.out.print(returnStr);

}

return returnStr;

}



public static void main(String[] args) {

// 测试key数

int allKeyCount = 1000;

// 默认节点数

int nodeCount = 10;

// 标准差的计算

TreeMap<Double,String> standardDeviationMap = new TreeMap<Double,String>();

int virtualNum = 0;

// 测试10万虚拟节点一下的标准差

int maxvirtualNum = 100000;

while(virtualNum<maxvirtualNum)

{

// 如果节点数超过10000,一次加10000来算

if(virtualNum>10000)

{

virtualNum+= 10000;

}

// 如果节点数超过1000,一次加1000来算

else if(virtualNum>1000)

{

virtualNum+= 1000;

}

else if(virtualNum>500)

{

virtualNum+= 100;

}

// 如果节点数小于500,一次加50来算

else

{

virtualNum+= 50;

}

LinkedHashMap<String,Integer> distribution = getDistribution(allKeyCount, nodeCount, virtualNum);

double standardDeviation = standardDeviation(allKeyCount, nodeCount, virtualNum, distribution);

String virtualNumStr = standardDeviationMap.get(standardDeviation);

if(null == virtualNumStr)

virtualNumStr = "";

virtualNumStr +=" "+virtualNum;

standardDeviationMap.put(standardDeviation,virtualNumStr);

}

for( Double s : standardDeviationMap.keySet())

{

System.out.println(String.format("虚拟节点数 %s 的标准差 %s", standardDeviationMap.get(s),s));

}

System.out.println("");

}

/**

* 获取各个服务器的key数量分布

* @param allCount

* @param nodeCount

* @param virtualNum

* @return

*/

public static LinkedHashMap<String,Integer> getDistribution(int allCount,int nodeCount,int virtualNum)

{

LinkedHashMap<String,Integer> distribution = new LinkedHashMap<String,Integer>();

List<String> serviceIpList = new ArrayList<String>();

for(int i=1;i<nodeCount+1;i++)

{

String serviceIp = "192.168.0."+i;

serviceIpList.add(serviceIp);

distribution.put(serviceIp, 0);

}

UniformityHash un = new UniformityHash(nodeCount,virtualNum,serviceIpList);

String key = "懒得看聚少离多222";

for(int i=0;i<allCount;i++)

{

String s = key+i;

String serviceIp = un.getServiceIp(s);

try

{

distribution.put(serviceIp, distribution.get(serviceIp)+1);

}

catch(Exception e)

{

e.printStackTrace();

}

//System.out.println(String.format("字符串: %s 对应的服务器是:%s",s,serviceIp));

}

/* for(String s : distribution.keySet())

{

System.out.println(String.format("服务器 %s 数量 %s", s, distribution.get(s)));

}*/

return distribution;

}

/**

* 计算标准差

* @param allCount

* @param nodeCount

* @param virtualNum

* @param distribution

* @return

*/

private static double standardDeviation(int allCount,int nodeCount,int virtualNum,LinkedHashMap<String,Integer> distribution)

{

double sum = 0;

// 平均数

double avgValue = allCount/nodeCount;

for(String key :distribution.keySet())

{

sum += Math.pow(distribution.get(key)-avgValue, 2);

}

return Math.sqrt(sum/(distribution.size()-1));

}

private final static int p = 16777619;

/**

* 使用FNV132HASH

* @param str

* @return

*/

private int getHash(String str)

{

int hash = (int)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)

return hash;

return Math.abs(hash);

}

}



用户头像

时来运转

关注

还未添加个人签名 2019.02.26 加入

还未添加个人简介

评论

发布
暂无评论
架构师训练营第5周作业