django 如何处理多个 memcached 服务器?

Posted

技术标签:

【中文标题】django 如何处理多个 memcached 服务器?【英文标题】:How does django handle multiple memcached servers? 【发布时间】:2011-10-16 02:48:03 【问题描述】:

在 django 文档中是这样说的:

...

Memcached 的一个出色功能是它能够共享缓存 多台服务器。这意味着您可以在多个服务器上运行 Memcached 守护进程 机器,并且程序会将机器组视为单个 缓存,无需在每台机器上复制缓存值。到 利用此功能,将所有服务器地址包含在 LOCATION,用分号分隔或作为一个列表。

...

Django's cache framework - Memcached

这究竟是如何工作的?我在这个网站上阅读了一些答案,这些答案表明这是通过基于键的散列在服务器之间进行分片来完成的。

Multiple memcached servers question

How does the MemCacheStore really work with multiple servers?

这很好,但我需要一个比这更具体和详细的​​答案。将 django 与 pylibmc 或 python-memcached 一起使用,这个分片实际上是如何执行的?配置设置中 IP 地址的顺序是否重要?如果运行同一个 django 应用程序的两个不同的 Web 服务器有两个不同的设置文件,其中 memcached 服务器的 IP 地址以不同的顺序排列怎么办?这是否会导致每台机器使用不同的分片策略,从而导致重复键和其他低效率?

如果某台机器在列表中出现两次怎么办?例如,如果我要做这样的事情,其中​​ 127.0.0.1 实际上与 172.19.26.240 是同一台机器?

CACHES = 
    'default': 
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': [
            '127.0.0.1:11211',
            '172.19.26.240:11211',
            '172.19.26.242:11211',
        ]
    

如果其中一个 memcached 服务器的容量比其他服务器大怎么办?如果机器 1 的内存缓存为 64MB,机器 2 的内存缓存为 128MB,分片算法是否会考虑这一点并给予机器 2 更大比例的密钥?

我还了解到,如果 memcached 服务器丢失,那么这些密钥也会丢失。当涉及分片时,这一点很明显。更重要的是,如果 memcached 服务器出现故障并且我将其 IP 地址留在设置文件中会发生什么? django/memcached 会简单地无法获取任何将被分片到该故障服务器的密钥,还是会意识到该服务器已发生故障并提出新的分片策略?如果有一个新的分片策略,它是智能地获取原本用于故障服务器的密钥并将它们分配给剩余的服务器,还是提出一个全新的策略,就好像第一台服务器不存在一样?导致密钥重复?

我尝试阅读 python-memcached 的源代码,但根本无法弄清楚这一点。我打算尝试阅读 libmemcached 和 pylibmc 的代码,但我想如果有人已经知道,在这里问会更容易。

【问题讨论】:

【参考方案1】:

如果使用两个不同的内存缓存是理想的,django 的默认实现允许这种行为。

首先你要更新你的 settings.py:

CACHES = 
    'default': 
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': '127.0.0.1:11211',
    ,
    'rusty': 
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': '127.0.0.1:11222',
    

在您的 django 代码中,访问 memcache 的默认方法没有改变。您现在可以按如下方式使用其他缓存接口:

from django.core.cache import cache, caches

cache.set("activity", 'great stuff', 15 ) # Default cache
caches["rusty"].set("activity", "A great time", 32) # New rusty cache interface

Django 文档中有一篇很棒的文章涵盖了这个主题:https://docs.djangoproject.com/en/dev/topics/cache/

【讨论】:

【参考方案2】:

在提出问题两年后考虑添加这个答案,因为它在搜索中的排名非常高,而且我们确实发现了 django 只与一个 memcached 服务器对话的情况。

当一个站点在 django 1.4.3 上运行时,python-memcached 1.51 与四个 memcached 实例通信,我们发现查询数据库的频率远高于预期。进一步挖掘,我们发现cache.get() 正在返回None 以获取已知存在于至少一个memcached 实例中的键。当使用 -vv 选项启动 memcached 时,它显示该问题仅针对一台服务器提出!

拉了很多头发后,我们将后端切换到django.core.cache.backends.memcached.PyLibMCCache (pylibmc),问题就消失了。

【讨论】:

【参考方案3】:

我测试了其中的一部分,并发现了 django 1.1 和 python-memcached 1.44 的一些有趣的东西。

在 django 上使用 2 个内存缓存服务器

cache.set('a', 1, 1000)

cache.get('a') # returned 1

我查看了哪个 memcache 服务器“a”被分片到使用另外 2 个 django 设置,每个设置都指向一个 memcache 服务器。我通过在原始 django 实例和存储“a”的 memcache 服务器之间设置防火墙来模拟连接中断。

cache.get('a') # paused for a few seconds and then returned None

cache.set('a', 2, 1000)

cache.get('a') # returned 2 right away

如果服务器出现故障,memcache 客户端库会更新其分片策略。

然后我删除了防火墙。

cache.get('a') # returned 2 for a bit until it detected the server back up then returned 1!

您可以在 memcache 服务器掉线又回来时读取陈旧数据! Memcache 没有采取任何聪明的措施来防止这种情况发生。

如果您使用的缓存策略将内容长时间放入 memcache 并依赖缓存失效来处理更新,这真的会搞砸事情。可以将旧值写入该键的“正常”缓存服务器,如果您在该窗口期间失去连接并且无效,当服务器再次可访问时,您将读取您不应该能够读取的陈旧数据到。

另外一点:我一直在阅读一些对象/查询缓存库,我认为 johnny-cache 应该不受这个问题的影响。它没有明确地使条目无效;相反,它会在表更改时更改缓存查询的键。所以它永远不会意外读取旧值。

编辑:我认为我关于 johnny-cache 工作正常的注释是废话。 http://jmoiron.net/blog/is-johnny-cache-for-you/ 说“每个请求都有额外的缓存读取以加载当前代”。如果世代存储在缓存本身中,上述场景可能会导致读取过时的世代。

【讨论】:

哇,这太迷人了,而且是我从未想过的。我猜你只需要清除所有出现故障的内存缓存服务器。很有帮助,谢谢! Sean 指出 [1] 也可能出现相反的问题:您还可以从未关闭的 memcached 服务器读取陈旧数据。似乎唯一安全的解决方案是在重新连接时将它们全部刷新。 [1]bugs.launchpad.net/python-memcached/+bug/887765/comments/9【参考方案4】:

执行分片的是实际的 memcached 客户端。 Django 仅将来自settings.CACHES 的配置传递给客户端。

服务器的顺序无关紧要*,但是(至少对于 python-memcached)您可以为每个服务器指定一个“权重”:

CACHES = 
    'default': 
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': [
                ('cache1.example.org:11211', 1),
                ('cache2.example.org:11211', 10),
            ],

我认为快速浏览一下memcache.py(来自python-memcached),尤其是memcached.Client._get_server,应该可以回答您的其余问题:

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

我希望其他 memcached 客户端以类似的方式实现。


@Apreche 的澄清:在一种情况下,服务器的顺序确实很重要。如果您有多个 Web 服务器,并且希望它们都将相同的密钥放在相同的 memcached 服务器上,则需要以相同的顺序使用相同的服务器列表和相同的权重配置它们

【讨论】:

谢谢!这并没有完全回答我所有的问题,但它指出了我自己回答这些问题的正确方向。您唯一弄错的是服务器的顺序在一种情况下确实很重要。如果您有多个 Web 服务器,并且希望它们都将相同的密钥放在相同的 memcached 服务器上,则需要以相同的顺序和相同的权重为它们配置相同的服务器列表。

以上是关于django 如何处理多个 memcached 服务器?的主要内容,如果未能解决你的问题,请参考以下文章

Django Fixtures 如何处理 ManyToManyFields?

memcached 如何处理容错的?

memcached如何处理容错的?

如何处理 Django 模型中的循环关系?

服务生如何处理并发任务?

如何处理 Django 中未应用的迁移?