回答

2

看着python-memcached v1.45上的_get_server方法,它似乎没有使用一致的哈希,而是一个简单的hash % len(buckets)

对于二进制协议也是如此,python-memcache使用,据我可以看到,只有文本命令。

+0

有没有什么办法让蟒蛇,使用memcached的一致性哈希? – Continuation 2010-04-13 14:17:27

+1

[hash_ring](http://pypi.python.org/pypi/hash_ring)有一个'memcache.Client'的子类实现一致的哈希,[从版本1.1](http://amix.dk/blog/post/ 19370)。 然后,您可以在django中实现[自定义缓存后端](http://docs.djangoproject.com/en/dev/topics/cache/#using-a-custom-cache-backend),继承原始memcached后端所以它使用'MemcacheRing'。 – 2010-04-13 15:40:21

+0

你真的确定它是否做到这一点?请看我上面的答案。 – adamJLev 2010-05-08 04:16:58

1

您可能能够使用此:http://amix.dk/blog/post/19370

它封装中的python-memcache中的客户端类,所以按键都采用了统一的散列分布。

编辑 - 我在挖python-memcached 1.4.5源代码,它看起来可能实际上支持一致的散列。 相关代码:

from binascii import crc32 # zlib version is not cross-platform 
def cmemcache_hash(key): 
    return((((crc32(key) & 0xffffffff) >> 16) & 0x7fff) or 1) 
serverHashFunction = cmemcache_hash 

-- SNIP -- 

def _get_server(self, key): 
    if isinstance(key, tuple): 
     serverhash, key = key 
    else: 
     serverhash = serverHashFunction(key) 

    for i in range(Client._SERVER_RETRIES): 
     server = self.buckets[serverhash % len(self.buckets)] 
     if server.connect(): 
      #print "(using server %s)" % server, 
      return server, key 
     serverhash = serverHashFunction(str(serverhash) + str(i)) 
    return None, None 

在此基础上的代码,它看起来像它确实实现了算法,除非cmemcache_hash不是一个有意义的名字,这是不是真正的算法。 (现在已经退役cmemcache做一致性散列)

但我认为OP指的是更具“弹性”的一致散列, libketama。我不认为这里有一个解决方案,看起来你需要卷起袖子编译/安装更高级的memcached库,如pylibmc,并编写一个自定义的Django后端,使用它来代替python- memcached的。

总之,在这两种情况下,将在您添加/删除水桶到池中出现按键的一些重映射(甚至libketama,只是比其他算法更少)

+1

对不起,但这是**不**一致哈希。 'cmemcache_hash'只是计算一个键的简单哈希值来选择将获取请求的服务器,并且您可以在此清楚地看到该库使用(加权)服务器列表上的简单模来执行此操作。另外,'MemcacheRing'的'libketama'正在实现相同的算法,但是'memcacheRing'及其执行'memcache.Client'已准备好进行集成。 – 2010-05-08 09:15:22

0

现在vbucket是解决一致性哈希来对缓存未命中的影响最小。

0

我已经使用了一致性哈希算法。丢失的钥匙是钥匙总数的1/n。这意味着成功的密钥获取将在85%左右达到6/7 * 100。 here

+0

链接只有答案是不鼓励的。 \t 请引用参考链接中答案的基本部分,因为如果链接的页面发生变化,答案可能会失效。 – 2015-09-15 10:34:52

1

请检查此示例python执行一致哈希。

实现主要:想象一个continnum圈与许多通过它传播复制的服务器分。当我们添加新的服务器,总 缓存键的1/n将丢失

'''consistent_hashing.py is a simple demonstration of consistent 
hashing.''' 

import bisect 
import hashlib 

class ConsistentHash: 
    ''' 

    To imagine it is like a continnum circle with a number of replicated 
    server points spread across it. When we add a new server, 1/n of the total 
    cache keys will be lost. 

    consistentHash(n,r) creates a consistent hash object for a 
    cluster of size n, using r replicas. 

    It has three attributes. num_machines and num_replics are 
    self-explanatory. hash_tuples is a list of tuples (j,k,hash), 
    where j ranges over machine numbers (0...n-1), k ranges over 
    replicas (0...r-1), and hash is the corresponding hash value, 
    in the range [0,1). The tuples are sorted by increasing hash 
    value. 

    The class has a single instance method, get_machine(key), which 
    returns the number of the machine to which key should be 
    mapped.''' 
    def __init__(self,replicas=1): 
     self.num_replicas = replicas 

    def setup_servers(self,servers=None): 
    hash_tuples = [(index,k,my_hash(str(index)+"_"+str(k))) \ 
       for index,server in enumerate(servers) 
       for k in range(int(self.num_replicas) * int(server.weight)) ] 
    self.hash_tuples=self.sort(hash_tuples); 

    def sort(self,hash_tuples): 
    '''Sort the hash tuples based on just the hash values ''' 
    hash_tuples.sort(lambda x,y: cmp(x[2],y[2])) 
    return hash_tuples 

    def add_machine(self,server,siz): 
    '''This mathod adds a new machine. Then it updates the server hash 
    in the continuum circle ''' 
    newPoints=[(siz,k,my_hash(str(siz)+"_"+str(k))) \ 
        for k in range(self.num_replicas*server.weight)] 
    self.hash_tuples.extend(newPoints) 
    self.hash_tuples=self.sort(self.hash_tuples); 



    def get_machine(self,key): 
    '''Returns the number of the machine which key gets sent to.''' 
    h = my_hash(key) 
    # edge case where we cycle past hash value of 1 and back to 0. 
    if h > self.hash_tuples[-1][2]: return self.hash_tuples[0][0] 
    hash_values = map(lambda x: x[2],self.hash_tuples) 
    index = bisect.bisect_left(hash_values,h) 
    return self.hash_tuples[index][0] 

def my_hash(key): 
    '''my_hash(key) returns a hash in the range [0,1).''' 
    return (int(hashlib.md5(key).hexdigest(),16) % 1000000)/1000000.0