KetamaHash一致性算法的C语言实现有哪些特点?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1597个文字,预计阅读时间需要7分钟。
最近在研究一致性HASH算法(Consistent Hashing),用于解决memcached集群中当服务器增减时对散列值的影响。在JAVEYE上的一篇文章中,找到了KetamaHash算法的JAVA实现。
最近在研究"一致性HASH算法"(Consistent Hashing),用于解决memcached集群中当服务器出现增减变动时对散列值的影响。后来在JAVAEYE上的一篇文章中,找到了其中的 KetamaHash 算法的JAVA实现(一种基于虚拟结点的HASH算法),于是为了加深理解,对照JAVA版本,用C#重写了一个。放到这里,如果大家感兴趣的话, 可以下载测试一下,如果发现写法有问题请及时告之我,以便我及时修正。
下面是对Ketama的介绍:
Here’showitworks:
*Takeyourlistofservers(eg:1.2.3.4:11211,5.6.7.8:11211,9.8.7.6:11211)
*Hasheachserverstringtoseveral(100-200)unsignedints
*Conceptually,thesenumbersareplacedonacirclecalledthecontinuum.(imagineaclockfacethatgoesfrom0to2^32)
*Eachnumberlinkstotheserveritwashashedfrom,soserversappearatseveralpointsonthecontinuum,byeachofthenumberstheyhashedto.
*Tomapakey->server,hashyourkeytoasingleunsignedint,andfindthenextbiggestnumberonthecontinuum.Theserverlinkedtothatnumberisthecorrectserverforthatkey.
*Ifyouhashyourkeytoavaluenear2^32andtherearenopointsonthecontinuumgreaterthanyourhash,returnthefirstserverinthecontinuum.
Ifyouthenaddorremoveaserverfromthelist,onlyasmallproportionofkeysendupmappingtodifferentservers.
我的理解,这类方法其实有点像大学里的微积分的思想(通过连续函数的取值区间来计算图形面积)。通过把已知的实结点(memcached服务IP端口)列表结成一个圆,然后在两两实结点之间“放置”尽可能多的虚拟节点(上面文中的unsigned ints), 假设用户数据映射在虚拟节点上(用户数据真正存储位置是在该虚拟节点代表的实际物理服务器上),这样就能抑制分布不均匀,最大限度地减小服务器增减时的缓存重新分布。如下图:
下面是添加结点时:
如这篇文章所说(总结一致性哈希(Consistent Hashing) ):
Consistent Hashing最大限度地抑制了hash键的重新分布。另外要取得比较好的负载均衡的效果,往往在服务器数量比较少的时候需要增加虚拟节点来保证服务器能均匀的分布在圆环上。因为使用一般的hash方法,服务器的映射地点的分布非常不均匀。使用虚拟节点的思想,为每个物理节点(服务器)在圆上分配100~200个点。这样就能抑制分布不均匀,最大限度地减小服务器增减时的缓存重新分布。用户数据映射在虚拟节点上,就表示用户数据真正存储位置是在该虚拟节点代表的实际物理服务器上。
了解了原理,下面看一下具体实现。
JAVA实现代码取自Spy Memcached client中的类,原文的作者也尽可能的对代码进行注释说明,所以让我剩了不少时间。
下面是相应的.NET实现(注释参考JAVA版本):
{
//原文中的JAVA类TreeMap实现了Comparator方法,这里我图省事,直接用了net下的SortedList,其中Comparer接口方法)
privateSortedList<long,string>ketamaNodes=newSortedList<long,string>();
privateHashAlgorithmhashAlg;
privateintnumReps=160;
//此处参数与JAVA版中有区别,因为使用的静态方法,所以不再传递HashAlgorithmalg参数
publicKetamaNodeLocator(List<string>nodes,intnodeCopies)
{
ketamaNodes=newSortedList<long,string>();
numReps=nodeCopies;
//对所有节点,生成nCopies个虚拟结点
foreach(stringnodeinnodes)
{
//每四个虚拟结点为一组
for(inti=0;i<numReps/4;i++)
{
//getKeyForNode方法为这组虚拟结点得到惟一名称
byte[]digest=HashAlgorithm.computeMd5(node+i);
/**Md5是一个16字节长度的数组,将16字节的数组每四个字节一组,分别对应一个虚拟结点,这就是为什么上面把虚拟结点四个划分一组的原因*/
for(inth=0;h<4;h++)
{
longm=HashAlgorithm.hash(digest,h);
ketamaNodes[m]=node;
}
}
}
}
publicstringGetPrimary(stringk)
{
byte[]digest=HashAlgorithm.computeMd5(k);
stringrv=GetNodeForKey(HashAlgorithm.hash(digest,0));
returnrv;
}
stringGetNodeForKey(longhash)
{
stringrv;
longkey=hash;
//如果找到这个节点,直接取节点,返回
if(!ketamaNodes.ContainsKey(key))
{
//得到大于当前key的那个子Map,然后从中取出第一个key,就是大于且离它最近的那个key说明详见:www.javaeye.com/topic/684087
vartailMap=fromcollinketamaNodes
wherecoll.Key>hash
selectnew{coll.Key};
if(tailMap==null||tailMap.Count()==0)
key=ketamaNodes.FirstOrDefault().Key;
else
key=tailMap.FirstOrDefault().Key;
}
rv=ketamaNodes[key];
returnrv;
}
}
下面的代码与JAVA中有所不同,它使用静态方法实现:
{
publicstaticlonghash(byte[]digest,intnTime)
{
longrv=((long)(digest[3+nTime*4]&0xFF)<<24)
|((long)(digest[2+nTime*4]&0xFF)<<16)
|((long)(digest[1+nTime*4]&0xFF)<<8)
|((long)digest[0+nTime*4]&0xFF);
returnrv&0xffffffffL;/*Truncateto32-bits*/
}
/**
*Getthemd5ofthegivenkey.
*/
publicstaticbyte[]computeMd5(stringk)
{
MD5md5=newMD5CryptoServiceProvider();
byte[]keyBytes=md5.ComputeHash(Encoding.UTF8.GetBytes(k));
md5.Clear();
//md5.update(keyBytes);
//returnmd5.digest();
returnkeyBytes;
}
}
下面是.net版本下的测试结果
分布平均性测试:测试随机生成的众多key是否会平均分布到各个结点上测试结果如下:
最上面一行是参数说明,节点数目,总共有多少key,每个节点应该分配key的比例是多少。下面是每个结点分配到key的数目和比例。 多次测试后发现,这个Hash算法的节点分布都在标准比例左右徘徊。
节点增删测试:在环上插入N个结点,每个节点nCopies个虚拟结点。随机生成众多key,在增删节点时,测试同一个key选择相同节点的概率,测试如果如下:
上面三行分别是正常情况,节点增加,节点删除情况下的节点数目。下面两行表示在节点增加和删除情况下,同一个key分配在相同节点上的比例(命中率)。
后来我修改了几次增删结点的数量,基本验证了JAVA那位仁兄所说的那样:
多次测试后发现,命中率与结点数目和增减的节点数量有关。同样增删结点数目情况下,结点多时命中率高。同样节点数目,增删结点越少,命中率越高。这些都与实际情况相符。
这里还有一些链接,都是介绍和讨论Consistent Hashing的,有兴趣的朋友可以看一下,呵呵:)
总结一致性哈希(Consistent Hashing)
Ketama一致性Hash算法学习(含Java代码)
还有淘宝核心系统团队博客的这篇文章,Dynamo 简介
好了,今天的文章就先到这里了,这里是相应语言的源码下载链接:javac#
原文链接:www.cnblogs.com/daizhj/archive/2010/08/24/1807324.html
BLOG: daizhj.cnblogs.com/
作者:daizhj,代震军
本文共计1597个文字,预计阅读时间需要7分钟。
最近在研究一致性HASH算法(Consistent Hashing),用于解决memcached集群中当服务器增减时对散列值的影响。在JAVEYE上的一篇文章中,找到了KetamaHash算法的JAVA实现。
最近在研究"一致性HASH算法"(Consistent Hashing),用于解决memcached集群中当服务器出现增减变动时对散列值的影响。后来在JAVAEYE上的一篇文章中,找到了其中的 KetamaHash 算法的JAVA实现(一种基于虚拟结点的HASH算法),于是为了加深理解,对照JAVA版本,用C#重写了一个。放到这里,如果大家感兴趣的话, 可以下载测试一下,如果发现写法有问题请及时告之我,以便我及时修正。
下面是对Ketama的介绍:
Here’showitworks:
*Takeyourlistofservers(eg:1.2.3.4:11211,5.6.7.8:11211,9.8.7.6:11211)
*Hasheachserverstringtoseveral(100-200)unsignedints
*Conceptually,thesenumbersareplacedonacirclecalledthecontinuum.(imagineaclockfacethatgoesfrom0to2^32)
*Eachnumberlinkstotheserveritwashashedfrom,soserversappearatseveralpointsonthecontinuum,byeachofthenumberstheyhashedto.
*Tomapakey->server,hashyourkeytoasingleunsignedint,andfindthenextbiggestnumberonthecontinuum.Theserverlinkedtothatnumberisthecorrectserverforthatkey.
*Ifyouhashyourkeytoavaluenear2^32andtherearenopointsonthecontinuumgreaterthanyourhash,returnthefirstserverinthecontinuum.
Ifyouthenaddorremoveaserverfromthelist,onlyasmallproportionofkeysendupmappingtodifferentservers.
我的理解,这类方法其实有点像大学里的微积分的思想(通过连续函数的取值区间来计算图形面积)。通过把已知的实结点(memcached服务IP端口)列表结成一个圆,然后在两两实结点之间“放置”尽可能多的虚拟节点(上面文中的unsigned ints), 假设用户数据映射在虚拟节点上(用户数据真正存储位置是在该虚拟节点代表的实际物理服务器上),这样就能抑制分布不均匀,最大限度地减小服务器增减时的缓存重新分布。如下图:
下面是添加结点时:
如这篇文章所说(总结一致性哈希(Consistent Hashing) ):
Consistent Hashing最大限度地抑制了hash键的重新分布。另外要取得比较好的负载均衡的效果,往往在服务器数量比较少的时候需要增加虚拟节点来保证服务器能均匀的分布在圆环上。因为使用一般的hash方法,服务器的映射地点的分布非常不均匀。使用虚拟节点的思想,为每个物理节点(服务器)在圆上分配100~200个点。这样就能抑制分布不均匀,最大限度地减小服务器增减时的缓存重新分布。用户数据映射在虚拟节点上,就表示用户数据真正存储位置是在该虚拟节点代表的实际物理服务器上。
了解了原理,下面看一下具体实现。
JAVA实现代码取自Spy Memcached client中的类,原文的作者也尽可能的对代码进行注释说明,所以让我剩了不少时间。
下面是相应的.NET实现(注释参考JAVA版本):
{
//原文中的JAVA类TreeMap实现了Comparator方法,这里我图省事,直接用了net下的SortedList,其中Comparer接口方法)
privateSortedList<long,string>ketamaNodes=newSortedList<long,string>();
privateHashAlgorithmhashAlg;
privateintnumReps=160;
//此处参数与JAVA版中有区别,因为使用的静态方法,所以不再传递HashAlgorithmalg参数
publicKetamaNodeLocator(List<string>nodes,intnodeCopies)
{
ketamaNodes=newSortedList<long,string>();
numReps=nodeCopies;
//对所有节点,生成nCopies个虚拟结点
foreach(stringnodeinnodes)
{
//每四个虚拟结点为一组
for(inti=0;i<numReps/4;i++)
{
//getKeyForNode方法为这组虚拟结点得到惟一名称
byte[]digest=HashAlgorithm.computeMd5(node+i);
/**Md5是一个16字节长度的数组,将16字节的数组每四个字节一组,分别对应一个虚拟结点,这就是为什么上面把虚拟结点四个划分一组的原因*/
for(inth=0;h<4;h++)
{
longm=HashAlgorithm.hash(digest,h);
ketamaNodes[m]=node;
}
}
}
}
publicstringGetPrimary(stringk)
{
byte[]digest=HashAlgorithm.computeMd5(k);
stringrv=GetNodeForKey(HashAlgorithm.hash(digest,0));
returnrv;
}
stringGetNodeForKey(longhash)
{
stringrv;
longkey=hash;
//如果找到这个节点,直接取节点,返回
if(!ketamaNodes.ContainsKey(key))
{
//得到大于当前key的那个子Map,然后从中取出第一个key,就是大于且离它最近的那个key说明详见:www.javaeye.com/topic/684087
vartailMap=fromcollinketamaNodes
wherecoll.Key>hash
selectnew{coll.Key};
if(tailMap==null||tailMap.Count()==0)
key=ketamaNodes.FirstOrDefault().Key;
else
key=tailMap.FirstOrDefault().Key;
}
rv=ketamaNodes[key];
returnrv;
}
}
下面的代码与JAVA中有所不同,它使用静态方法实现:
{
publicstaticlonghash(byte[]digest,intnTime)
{
longrv=((long)(digest[3+nTime*4]&0xFF)<<24)
|((long)(digest[2+nTime*4]&0xFF)<<16)
|((long)(digest[1+nTime*4]&0xFF)<<8)
|((long)digest[0+nTime*4]&0xFF);
returnrv&0xffffffffL;/*Truncateto32-bits*/
}
/**
*Getthemd5ofthegivenkey.
*/
publicstaticbyte[]computeMd5(stringk)
{
MD5md5=newMD5CryptoServiceProvider();
byte[]keyBytes=md5.ComputeHash(Encoding.UTF8.GetBytes(k));
md5.Clear();
//md5.update(keyBytes);
//returnmd5.digest();
returnkeyBytes;
}
}
下面是.net版本下的测试结果
分布平均性测试:测试随机生成的众多key是否会平均分布到各个结点上测试结果如下:
最上面一行是参数说明,节点数目,总共有多少key,每个节点应该分配key的比例是多少。下面是每个结点分配到key的数目和比例。 多次测试后发现,这个Hash算法的节点分布都在标准比例左右徘徊。
节点增删测试:在环上插入N个结点,每个节点nCopies个虚拟结点。随机生成众多key,在增删节点时,测试同一个key选择相同节点的概率,测试如果如下:
上面三行分别是正常情况,节点增加,节点删除情况下的节点数目。下面两行表示在节点增加和删除情况下,同一个key分配在相同节点上的比例(命中率)。
后来我修改了几次增删结点的数量,基本验证了JAVA那位仁兄所说的那样:
多次测试后发现,命中率与结点数目和增减的节点数量有关。同样增删结点数目情况下,结点多时命中率高。同样节点数目,增删结点越少,命中率越高。这些都与实际情况相符。
这里还有一些链接,都是介绍和讨论Consistent Hashing的,有兴趣的朋友可以看一下,呵呵:)
总结一致性哈希(Consistent Hashing)
Ketama一致性Hash算法学习(含Java代码)
还有淘宝核心系统团队博客的这篇文章,Dynamo 简介
好了,今天的文章就先到这里了,这里是相应语言的源码下载链接:javac#
原文链接:www.cnblogs.com/daizhj/archive/2010/08/24/1807324.html
BLOG: daizhj.cnblogs.com/
作者:daizhj,代震军

