最近在研究"一致性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. webpack最佳入门实践系列(2)

    3.插件 在前端迅速发展的今天,许多没有太多技术含量并且感觉是在浪费时间的事情,就可以交给构建工具来做,例如:我们去手动创建index.html,手动引入打包好的js文件等操作,都可以叫个webpac ...

  2. pat 甲级 1022. Digital Library (30)

    1022. Digital Library (30) 时间限制 1000 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue A Di ...

  3. 访问外网 ML2 的配置

    通过 router 可以实现位于不同 vlan 中的 instance 之间的通信. 接下来要探讨的问题是 instance 如何与外部网络通信. 这里的外部网络是指的租户网络以外的网络. 租户网络是 ...

  4. Swing要点

    1. 布局管理器会控制潜逃在其他组件中组件的大小和位置. 2. 当某个组件加到背景组件上面时,被加入的组件是由背景组件的布局管理器管理的. 3. 布局管理器在做决定之前会询问组件的理想大小(setSi ...

  5. Nhibernate基本的增删改查实践

    1.链接配置 <?xml version="1.0" encoding="utf-8" ?> <hibernate-configuration ...

  6. depletion mosfet 的 depletion 解釋

    Origin mosfet 除了有 n channel 及 p channel 外, 還分為 enhanced 及 depletion 兩種, 引起我注意的是, depletion 代表什麼, Exp ...

  7. javascript 移动鼠标高亮显示改行

    主要用到两个事件 onmouseover onmouseout <!DOCTYPE html> <html> <head> <meta charset=&qu ...

  8. AC日记——[SCOI2007]蜥蜴 bzoj 1066

    1066 思路: 网络流最大流: 拆点,每个点拆成两个,流量为这个点的高度: 注意,文中说的距离是曼哈顿距离(劳资以为开根号wa了不知道多少次): 每两个距离不大于d的点连边,流量inf: 如果距离能 ...

  9. Codeforces Gym101502 E.The Architect Omar-find()函数

    E. The Architect Omar   time limit per test 1.0 s memory limit per test 256 MB input standard input ...

  10. 身份识别协议枚举工具ident-user-enum

    身份识别协议枚举工具ident-user-enum   身份识别协议(Ident protocol,IDENT)是一种Internet协议,用于识别使用特定TCP端口的用户身份.服务器开启该服务后,会 ...