最近在研究"一致性HASH算法"(Consistent Hashing),用于解决memcached集群中当服务器出现增减变动时对散列值的影响。后来 在JAVAEYE上的一篇文章中,找到了其中的 KetamaHash 算法的JAVA实现(一种基于虚拟结点的HASH算法),于是为了加深理解,对照 JAVA版本,用C#重写了一个。放到这里,如果大家感兴趣的话, 可以下载测试一下,如果发现写法有问题请及时告之我,以便我及时修正。 
 
      下面是对Ketama的介绍:


 Ketama is an implementation of a consistent hashing algorithm, meaning you can add or remove servers from the memcached pool without causing a complete remap of all keys. 
 Here’s how it works: 
 * Take your list of servers (eg: 1.2.3.4:11211, 5.6.7.8:11211, 9.8.7.6:11211) 
 * Hash each server string to several (100-200) unsigned ints 
 * Conceptually, these numbers are placed on a circle called the continuum. (imagine a clock face that goes from 0 to 2^32) 
 * Each number links to the server it was hashed from, so servers appear at several points on the continuum, by each of the numbers they hashed to. 
 * To map a key->server, hash your key to a single unsigned int, and find the next biggest number on the continuum. The server linked to that number is the correct server for that key. 
 * If you hash your key to a value near 2^32 and there are no points on the continuum greater than your hash, return the first server in the continuum. 
 If you then add or remove a server from the list, only a small proportion of keys end up mapping to different servers.

我的理解,这类方法其实有点像大学里的微积分的思想(通过连续函数的取值区间来计算图形面积)。通过把已知的实结点(memcached服务IP端口)列表结成一个圆,然后在两两实结点之间“放置”尽可能多的虚拟节点(上面文中的unsigned
ints),
假设用户数据映射在虚拟节点上(用户数据真正存储位置是在该虚拟节点代表的实际物理服务器上),这样就能抑制分布不均匀,最大限度地减小服务器增减时的缓存重新分布。如下图:

下面是添加结点时:
    
    

如这篇文章所说(总结一致性哈希(Consistent Hashing)):
   
      Consistent

Hashing最大限度地抑制了hash键的重新分布。另外要取得比较好的负载均衡的效果,往往在服务器数量比较少的时候需要增加虚拟节点来保证服务器能均匀的分布在圆环上。因为使用一般的hash方法,服务器的映射地点的分布非常不均匀。使用虚拟节点的思想,为每个物理节点(服务器)在圆上分配100~200个点。这样就能抑制分布不均匀,最大限度地减小服务器增减时的缓存重新分布。用户数据映射在虚拟节点上,就表示用户数据真正存储位置是在该虚拟节点代表的实际物理服务器上。

了解了原理,下面看一下具体实现。

JAVA实现代码取自Spy Memcached client中的类,原文的作者也尽可能的对代码进行注释说明,所以让我剩了不少时间。
   
       下面是相应的.NET实现(注释参考JAVA版本):


public class KetamaNodeLocator
{
    //原文中的JAVA类TreeMap实现了Comparator方法,这里我图省事,直接用了net下的SortedList,其中Comparer接口方法)
    private SortedList<long, string> ketamaNodes = new SortedList<long, string>();
    private HashAlgorithm hashAlg;
    private int numReps = 160;     //此处参数与JAVA版中有区别,因为使用的静态方法,所以不再传递HashAlgorithm alg参数
    public KetamaNodeLocator(List<string> nodes, int nodeCopies)
    {
        ketamaNodes = new SortedList<long, string>();         numReps = nodeCopies;
        //对所有节点,生成nCopies个虚拟结点
        foreach (string node in nodes)
        {
            //每四个虚拟结点为一组
            for (int i = 0; i < numReps / 4; i++)
            {
                //getKeyForNode方法为这组虚拟结点得到惟一名称 
                byte[] digest = HashAlgorithm.computeMd5(node + i);
                /** Md5是一个16字节长度的数组,将16字节的数组每四个字节一组,分别对应一个虚拟结点,这就是为什么上面把虚拟结点四个划分一组的原因*/  
                for (int h = 0; h < 4; h++)
                {
                    long m = HashAlgorithm.hash(digest, h);
                    ketamaNodes[m] = node;
                }
            }
        }
    }     public string GetPrimary(string k)
    {
        byte[] digest = HashAlgorithm.computeMd5(k);
        string rv = GetNodeForKey(HashAlgorithm.hash(digest, 0));
        return rv;
    }     string GetNodeForKey(long hash)
    {
        string rv;
        long key = hash;
        //如果找到这个节点,直接取节点,返回   
        if (!ketamaNodes.ContainsKey(key))
        {
            //得到大于当前key的那个子Map,然后从中取出第一个key,就是大于且离它最近的那个key 说明详见: http://www.javaeye.com/topic/684087
            var tailMap = from coll in ketamaNodes
                          where coll.Key > hash
                          select new { coll.Key };
            if (tailMap == null || tailMap.Count() == 0)
                key = ketamaNodes.FirstOrDefault().Key;
            else
                key = tailMap.FirstOrDefault().Key;
        }
        rv = ketamaNodes[key];
        return rv;    }}

下面的代码与JAVA中有所不同,它使用静态方法实现:


public class HashAlgorithm
{
    public static long hash(byte[] digest, int nTime)
    {
        long rv = ((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);         return rv & 0xffffffffL; /* Truncate to 32-bits */
    }     /**
     * Get the md5 of the given key.
     */
    public static byte[] computeMd5(string k)
    {
        MD5 md5 = new MD5CryptoServiceProvider();
       
        byte[] keyBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(k));
        md5.Clear();
        //md5.update(keyBytes);
        //return md5.digest();
        return keyBytes;
    }
}

下面是.net版本下的测试结果
   
       分布平均性测试:测试随机生成的众多key是否会平均分布到各个结点上测试结果如下:     
    
         
    
      最上面一行是参数说明,节点数目,总共有多少key,每个节点应该分配key的比例是多少。下面是每个结点分配到key的数目和比例。 多次测试后发现,这个Hash算法的节点分布都在标准比例左右徘徊。

节点增删测试:在环上插入N个结点,每个节点nCopies个虚拟结点。随机生成众多key,在增删节点时,测试同一个key选择相同节点的概率,测试如果如下:

上面三行分别是正常情况,节点增加,节点删除情况下的节点数目。下面两行表示在节点增加和删除情况下,同一个key分配在相同节点上的比例(命中率)。

后来我修改了几次增删结点的数量,基本验证了JAVA那位仁兄所说的那样:
    
      多次测试后发现,命中率与结点数目和增减的节点数量有关。同样增删结点数目情况下,结点多时命中率高。同样节点数目,增删结点越少,命中率越高。这些都与实际情况相符。

这里还有一些链接,都是介绍和讨论Consistent Hashing的,有兴趣的朋友可以看一下,呵呵:)
    
     总结一致性哈希(Consistent Hashing)
    
     Ketama一致性Hash算法学习(含Java代码)     
    
     还有淘宝核心系统团队博客的这篇文章, Dynamo 简介    
    
     好了,今天的文章就先到这里了,这里是相应语言的源码下载链接: java   c#
    
       原文链接:http://www.cnblogs.com/daizhj/archive/2010/08/24/1807324.html

BLOG: http://daizhj.cnblogs.com/

一致性哈希算法(c#版)的更多相关文章

  1. _00013 一致性哈希算法 Consistent Hashing 新的讨论,并出现相应的解决

    笔者博文:妳那伊抹微笑 博客地址:http://blog.csdn.net/u012185296 个性签名:世界上最遥远的距离不是天涯,也不是海角,而是我站在妳的面前.妳却感觉不到我的存在 技术方向: ...

  2. 一致性哈希算法与Java实现

    原文:http://blog.csdn.net/wuhuan_wp/article/details/7010071 一致性哈希算法是分布式系统中常用的算法.比如,一个分布式的存储系统,要将数据存储到具 ...

  3. 五分钟理解一致性哈希算法(consistent hashing)

    转载请说明出处:http://blog.csdn.net/cywosp/article/details/23397179 一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法 ...

  4. 每天进步一点点——五分钟理解一致性哈希算法(consistent hashing)

    转载请说明出处:http://blog.csdn.net/cywosp/article/details/23397179     一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT) ...

  5. 一致性哈希算法以及其PHP实现

    在做服务器负载均衡时候可供选择的负载均衡的算法有很多,包括:  轮循算法(Round Robin).哈希算法(HASH).最少连接算法(Least Connection).响应速度算法(Respons ...

  6. Java_一致性哈希算法与Java实现

    摘自:http://blog.csdn.net/wuhuan_wp/article/details/7010071 一致性哈希算法是分布式系统中常用的算法.比如,一个分布式的存储系统,要将数据存储到具 ...

  7. 一致性哈希算法(consistent hashing)【转】

    一致性哈希算法 来自:http://blog.csdn.net/cywosp/article/details/23397179       一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希 ...

  8. 一致性哈希算法学习及JAVA代码实现分析

    1,对于待存储的海量数据,如何将它们分配到各个机器中去?---数据分片与路由 当数据量很大时,通过改善单机硬件资源的纵向扩充方式来存储数据变得越来越不适用,而通过增加机器数目来获得水平横向扩展的方式则 ...

  9. 一致性哈希算法——算法解决的核心问题是当slot数发生变化时,能够尽量少的移动数据

    一致性哈希算法 摘自:http://blog.codinglabs.org/articles/consistent-hashing.html 算法简述 一致性哈希算法(Consistent Hashi ...

随机推荐

  1. DFA NFA

    如果不用 DFA, NFA,我觉得也是可以处理编译过程的,一个字符一个字符的读入,并结合上下文,来确定 token

  2. 【CF1043C】Smallest Word(构造)

    题意:给定一个由a和b构成的字符串,可以选择翻转或不翻转他的每个前缀,翻转记为1不翻转记为0,求能将字符串排序的字典序最小的操作序列 n<=1e3 思路:考虑极长的一段a [t,w] 翻转t-1 ...

  3. 常用 vim 命令总结

    学习 vim ,是需要经常使用的,而这些命令,是我这段时间最常用的命令,很有效率的提高的我的文本编辑效率.----------------------------------------------- ...

  4. django 模型生成sql(多对多)

    模型如下: class Publisher(models.Model): name = models.CharField(max_length=30) address = models.CharFie ...

  5. AC日记——3的幂的和 51nod 1013

    3的幂的和 思路: 矩阵快速幂:   sn-1      3 1        sn   *          = 1     0 1  1 来,上代码: #include <cstdio> ...

  6. 转载:html+js实现只允许输入两位小数的输入框

    JS代码: <script language="JavaScript" type="text/javascript"> function clear ...

  7. Java多线程之Thread、Runnable、Callable及线程池

    一.多线程 线程是指进程中的一个执行流程,一个进程中可以有多个线程.如java.exe进程中可以运行很多线程.进程是运行中的程序,是内存等资源的集合,线程是属于某个进程的,进程中的多个线程共享进程中的 ...

  8. POJ 3070 Fibonacci【斐波那契数列/矩阵快速幂】

    Fibonacci Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 17171   Accepted: 11999 Descr ...

  9. GLB串

    题目描述 只要一个字符串中包含大写“GLB”,高老板就认为这是一个GLB串.现在给你一些字符串,请你帮高老板判断这些字符串是不是GLB串. 输入 首先是一个整数T,表示T行数据,每行一个字符串(只包括 ...

  10. 代码Rework中的反思

    以前编码只是关注能写出来,并让程序运行就完事,这是非常错误的想法. 让我们重新思考软件设计中的一些问题吧! 软件设计就像设计房屋,设计器具,是一个道理.软件的复杂度和bug完全是自己造成的,要设计好的 ...