zoukankan      html  css  js  c++  java
  • 一致性hash 之 [翻译]Consistent Hash By Tom White

    http://ptsolmyr.com/2010/07/30/consistent_hash_by_tom_white 

    http://sandaobusi.iteye.com/blog/964368   推荐 java例子

    http://martinbroadhurst.com/Consistent-Hash-Ring.html   推荐C++实现

    http://www.yeeach.com/2009/10/02/consistent-hashing%E7%AE%97%E6%B3%95/

      

    Tom White是<Hadoop: The Definitive Guide>的作者,目前在云计算大牛公司cloudera就职。本篇博客是原文翻译了Tom White的一篇介绍一致性哈希的文章,本人英语,汉语都很悲剧,有不当的地方尽可拍砖。

    原文 link: http://www.lexemetech.com/2007/11/consistent-hashing.html

    —————————————————–

    我最近一段时间在研究 consistent hash。介绍它的paper(Consistent Hashing and Random Trees: Distributed Caching Protocols for Relieving Hot Spots on the World Wide Web by David Karger et al) 十年前就出现了,不过直到最近才悄悄的有越来越多的service开始使用consistent hash,这些service包括Amazon’s Dynamo,以及memcached (向Last.fm敬礼)。那么到底什么是consistent hash呢?大家为什么要关注它呢?

    consistent hash的需求来自于运行一个cache集群(例如web cache)时遇到的一些限制。如果你拥有一个由n台cache机器组成的集群,那么最普通的load balance方式就是把进来的对象o放在编号为hash(o) mod n的那一台上。你会觉得这个方案简介优美,直到有一天,由于种种原因你不得不增加或者移除一些cache机器,这时,集群的机器数目n变了,每个对象都被hash求余到了新的机器。这将是一场灾难,因为真正存放内容的server会被来自于cache集群的request拖垮。这时整个系统看起来就像没有cache一样。这就是大家为什么关心consistent hash,因为大家需要使用它来避免系统被拖垮。

    情况如果是这样的就好了:当集群添加了一台cache机器,该机器只从其他cache机器中读取应得的那些对象;相应的,当一个cache机器从集群中移除,最好是它cache住的对象被分配给其他的cache机器(而没有更多的数据移动)。这种理想的情境就是consistent hash所追求并实现的:如果可能的话,始终将同一组对象分配给相同的机器。

    consistent hash算法背后最基础的思想就是:对object和cache machine使用相同的hash函数。这样做的好处是能够把cache机器映射到一段interval上,而这段interval就会包含一定数目的对象的hash值。如果某台cache机器被移除了,那么它映射到的interval被和它相邻的一个cache机器托管,其他所有的cache机器都不用变。

    描述

    让我们来更深入的来了解一下consistent hash。Hash的作用就是把object和cache映射到一个数值范围上。Java程序员对hash应该很熟悉了–每个对象的hashCode方法会返回一个在[-231, 231-1]的int型整数。我们把这个数值范围首尾相接的映射到一个环上。下图描述了一组object(1, 2, 3, 4)和一组cache(A, B, C)分别映射在Hash环上。(图片源于Web Caching with Consistent Hashing by David Karger et al)

    图1

    要确定某个object会缓存在哪个cache,我们从这个object开始顺时针前进,知道我们遇到一个cache点。这样,从上图的例子我们看到object 1和4 归cache A,object 2归cache B,而cache C缓存的事object 3。考虑一下,当cache C被移除了,会发生什么?在这种情况下,object 3被cache A缓存住,所有其他object都不用移动。如果如图2,cache集群添加了cache D,那么D会缓存object 3和4,把object 1留给A。

    图2

    一切都很好,除了一点:指派给每个cache的间距大小太随机了,这样就会object的分配也极度的不均匀。 为了解决这个问题,我们引入”virtual nodes”这个概念:即每个cache在hash环上有多个副本,也就是说,每当我们加入一个cache,在环上都会为这个cache增加多个点。

    我下面的代码做了一个仿真实验,将10,000个object存入10个cache,你会在下面的plot图中看到virtual nodes的影响。x轴上是每个cache的副本数(对数刻度)。当x值较小时,我们看到objects在caches中的分布是不平衡的(y轴是以百分比形式表示objects在caches中分布的标准差)。随着cache的replica的增加,objects的分布趋向于更加平衡。这个实验说明了每个cache大概100-200的replica能够使object的分布较为平衡(标准差在5%-10%)

    experiment result

    实现

    下面是使用Java的一个简单实现。要使consistent hash的效果明显,很重要的一点是使用一个mix的很好的hash函数。Java中object的hashCode方法的大多数实现都没有提供很好的mix性能。所以我们提供一个HashFunction接口,以便于定制使用的hash函数。在这里推荐MD5.

    import java.util.Collection;
    import java.util.SortedMap;
    import java.util.TreeMap;
     
    public class ConsistentHash {
     
      
    private final HashFunction hashFunction;
      
    private final int numberOfReplicas;
      
    private final SortedMap circle =
        
    new TreeMap();
     
      
    public ConsistentHash(HashFunction hashFunction,
        
    int numberOfReplicas, Collection nodes) {
     
        
    this.hashFunction = hashFunction;
        
    this.numberOfReplicas = numberOfReplicas;
     
        
    for (T node : nodes) {
          add(node);
        }
      }
     
      
    public void add(T node) {
        
    for (int i = 0; i < numberOfReplicas; i++) {
          circle.put(hashFunction.hash(node.toString() 
    + i),
            node);
        }
      }
     
      
    public void remove(T node) {
        
    for (int i = 0; i < numberOfReplicas; i++) {
          circle.remove(hashFunction.hash(node.toString() 
    + i));
        }
      }
     
      
    public T get(Object key) {
        
    if (circle.isEmpty()) {
          
    return null;
        }
        
    int hash = hashFunction.hash(key);
        
    if (!circle.containsKey(hash)) {
          SortedMap tailMap 
    =
            circle.tailMap(hash);
          hash 
    = tailMap.isEmpty() ?
                 circle.firstKey() : tailMap.firstKey();
        }
        
    return circle.get(hash);
      } 
     

    } 

    上面的代码用一个integer的sorted map来表示hash circle。当ConsistentHash创建时,每个node都被添加到circle map中(添加的次数由numberOfReplicas控制)。每个replica的位置,由node的名字加上一个数字后缀所对应的hash值来决定。
    要为一个object找到它应该去的node(get方法),我们把object的hash值放入map中查找。大多数情况下,不会恰好有一个node和这个object重合(即使每个node都有一定量的replica,hash的值空间也比node数要多得多),所以用tailMap方法找到map中的下一个key。如果tail map为空,那么我们转一圈,找到circle中的第一个key。

    使用

    那么你应该如何使用consistent hash呢?一般情况下,你可以使用一些library,而不是自己去写代码。例如上面提到的memcached–一个分布式的内存cache系统,现在已经有了支持consisitent hash的client。由Last.fm的Richard Jones实现的ketama是第一个,现在是有Dustin Sallings贡献的Java实现。很有趣的是只有客户端需要实现consisitent hash算法,server端的代码不需要任何改变。其他使用consisitent hash的系统有Chord,一个分布式hash表的实现,和Amazon的Dynamo,一个key-value存储系统。(没有开源) 

  • 相关阅读:
    NYOJ 10 skiing DFS+DP
    51nod 1270 数组的最大代价
    HDU 4635 Strongly connected
    HDU 4612 Warm up
    POJ 3177 Redundant Paths
    HDU 1629 迷宫城堡
    uva 796
    uva 315
    POJ 3180 The Cow Prom
    POJ 1236 Network of Schools
  • 原文地址:https://www.cnblogs.com/xuxm2007/p/2153638.html
Copyright © 2011-2022 走看看