Как django обрабатывает несколько серверов memcached? - PullRequest
19 голосов
/ 29 июля 2011

В документации по Django сказано:

...

Одна отличная особенность Memcached - это возможность совместно использовать кеш несколько серверов. Это означает, что вы можете запускать демоны Memcached на нескольких машины, и программа будет рассматривать группу машин как единый кеш, без необходимости дублировать значения кеша на каждой машине. к воспользоваться этой функцией, включить все адреса серверов в Расположение, разделенное точкой с запятой или списком.

...

Структура кэша Django - Memcached

Как именно это работает? Я читал некоторые ответы на этом сайте, которые предполагают, что это достигается путем разделения серверов на основе хэшей ключей.

Вопрос о нескольких серверах memcached

Как MemCacheStore действительно работает с несколькими серверами?

Хорошо, но мне нужен гораздо более конкретный и подробный ответ, чем этот. Используя django с pylibmc или python-memcached, как на самом деле выполняется этот шардинг? Имеет ли значение порядок IP-адресов в настройках конфигурации? Что если два разных веб-сервера, на которых запущено одно и то же приложение django, имеют два разных файла настроек с IP-адресами серверов memcached в другом порядке? Приведет ли это к тому, что на каждой машине будет использоваться отдельная стратегия разделения, что приведет к дублированию ключей и другим недостаткам?

Что, если конкретная машина появится в списке дважды? Например, что если бы я сделал что-то вроде этого, где 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 установлено 64 МБ кэша памяти, а на компьютере 2 - 128 МБ, примет ли это алгоритм разбиения и даст ли компьютеру 2 большую долю ключей?

Я также читал, что если сервер memcached потерян, то эти ключи будут потеряны. Это очевидно при использовании шардинга. Что важнее, что произойдет, если сервер memcached выйдет из строя и я оставлю его IP-адрес в файле настроек? Будет ли django / memcached просто не в состоянии получить какие-либо ключи, которые были бы защищены этим отказавшим сервером, или он поймет, что сервер вышел из строя, и предложит новую стратегию разделения? Если существует новая стратегия разделения, то разумно ли она берет ключи, которые изначально предназначались для отказавшего сервера, и делит их между оставшимися серверами, или она предлагает совершенно новую стратегию, как если бы первый сервер не существовал и ли дублировать ключи?

Я попытался прочитать исходный код python-memcached, но никак не мог понять это. Я планирую попробовать прочитать код libmemcached и pylibmc, но я решил, что спросить здесь будет проще, если кто-то уже знает.

Ответы [ 4 ]

13 голосов
/ 03 августа 2011

Это реальный клиент 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: Порядок серверов имеет значение в одном случае. Если у вас есть несколько веб-серверов, и вы хотите, чтобы все они помещали одинаковые ключи на одни и те же серверы memcached, вам необходимо настроить их для одного и того же списка серверов в том же порядке с одинаковыми весами

5 голосов
/ 28 октября 2011

Я протестировал часть этого и нашел некоторые интересные вещи с django 1.1 и python-memcached 1.44.

На django, используя 2 сервера memcache

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

cache.get('a') # returned 1

Я посмотрел, на каком сервере memcache 'a' были установлены 2 другие установки django, каждая из которых указывает на один из серверов memcache.Я смоделировал разрыв соединения, установив межсетевой экран между исходным экземпляром django и сервером memcache, в котором хранился «a».

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/ говорит "есть дополнительные чтения кеша на каждый запрос для загрузки текущих поколений".Если поколения хранятся в самом кэше, приведенный выше сценарий может привести к чтению устаревшего поколения.

2 голосов
/ 27 июля 2013

Мысль добавить этот ответ через два года после того, как вопрос задан, так как он занимает очень высокое место в поиске и потому что мы нашли ситуацию, когда django общался только с одним из серверов memcached.

На сайте, работающем на django 1.4.3, python-memcached 1.51, в котором используются четыре экземпляра memcached, мы обнаружили, что база данных запрашивается гораздо чаще, чем ожидалось. Копая дальше, мы обнаружили, что cache.get() возвращал None для ключей, которые, как было известно, присутствовали по крайней мере в одном из экземпляров memcached. Когда memcached запускался с опцией -vv, это показало, что вопрос был задан только одному серверу!

После того, как много волос было выдернуто, мы переключили бэкэнд на django.core.cache.backends.memcached.PyLibMCCache (pylibmc), и проблема исчезла.

1 голос
/ 23 апреля 2018

Если использование двух разных memcache идеально, реализация django по умолчанию допускает такое поведение.

Сначала вы захотите обновить настройки.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/

...