Кэш в памяти — самый дешёвый способ снять с базы данных повторяющиеся запросы. Memcached хранит пары «ключ — значение» в оперативной памяти и отвечает за доли миллисекунды, но ничего не знает о вашей схеме, не умеет хранить данные на диске и не реплицируется.

Установка и базовая настройка

В Debian и Ubuntu пакет ставится из штатного репозитория вместе с утилитами диагностики. Служба запускается от отдельного пользователя и слушает только localhost — это разумная настройка, которую не стоит менять.

sudo apt install memcached libmemcached-tools
sudo systemctl enable --now memcached
systemctl status memcached --no-pager
ss -lntp | grep 11211

В Debian параметры хранятся в /etc/memcached.conf: объём памяти, адрес, порт, число соединений. После правки перезапустите службу и проверьте, что применились именно ваши значения, а не аргументы из unit-файла.

grep -E '^-m|^-c|^-I' /etc/memcached.conf
sudo systemctl restart memcached
echo stats settings | nc -w 1 127.0.0.1 11211
journalctl -u memcached -n 30 --no-pager

stats settings — самый надёжный способ увидеть реальные maxbytes, maxconns и item_size_max. Если число не совпало с конфигом, проверьте systemctl cat memcached.

Проверка протокола вручную

Протокол текстовый, поэтому проверку можно сделать без клиентской библиотеки. Команда set принимает ключ, флаги, время жизни в секундах и длину значения; get возвращает значение и признак END.

printf 'set demo:key 0 60 5\r\nhello\r\nget demo:key\r\n' | nc -w 1 127.0.0.1 11211
printf 'add demo:key 0 60 5\r\nworld\r\n' | nc -w 1 127.0.0.1 11211
printf 'delete demo:key\r\n' | nc -w 1 127.0.0.1 11211

Время жизни 0 означает «хранить до вытеснения», а значение больше 30 дней трактуется как Unix-время. Флаги memcached не интерпретирует: клиент использует их для своих нужд, например для признака сжатия. Команда add пишет только при отсутствии ключа, replace — только при наличии.

Подключение из приложения

Схема cache-aside: приложение сначала спрашивает кэш, при промахе читает базу и записывает результат с TTL. Ошибка кэша не должна ронять запрос — иначе падение memcached превращается в отказ всего сайта.

from pymemcache.client.base import Client

cache = Client(("127.0.0.1", 11211), connect_timeout=0.2,
               timeout=0.2, ignore_exc=True)

def get_user(uid):
    key = f"user:{uid}:v1"
    data = cache.get(key)
    if data is None:
        data = load_user_from_db(uid)
        cache.set(key, data, expire=300)
    return data

В Python у pymemcache за это отвечает ignore_exc=True, а таймауты стоит выставить короткими, чтобы зависший кэш не держал воркеры. В PHP аналогично проверяйте результат на false: расширение возвращает его и при промахе, и при ошибке соединения.

$m = new Memcached();
$m->addServer('127.0.0.1', 11211);
$user = $m->get('user:42:v1');
if ($user === false) {
    $user = loadUser(42);
    $m->set('user:42:v1', $user, 300);
}

Ключи, TTL и инвалидация

Ключ ограничен 250 байтами и не должен содержать пробелов и управляющих символов. Рабочая схема — префикс приложения, сущность, идентификатор и версия формата: shop:product:1488:v3. Версия позволяет мгновенно перейти на новую структуру данных, не удаляя старые записи.

  • TTL задавайте от допустимой свежести данных, а не «на всякий случай».
  • Добавляйте случайный разброс в 5–10%, чтобы ключи не истекали одновременно.
  • Инвалидируйте запись после изменения в базе, а не по расписанию.
  • Не используйте flush_all в рабочем кластере без понимания последствий.
  • Для длинных составных ключей считайте хеш, но храните исходный ключ в логах.

Массовое истечение ключей вызывает лавину запросов к базе. Кроме разброса TTL помогает блокировка на пересчёт: один процесс обновляет значение, остальные короткое время отдают устаревшее.

import random

ttl = 300 + random.randint(0, 30)
cache.set(key, data, expire=ttl)

Память и вытеснение

Memcached выделяет память страницами и делит её на классы с разным размером блока. Значения попадают в подходящий класс, поэтому смесь мелких и крупных объектов расходует память менее эффективно, чем однородная нагрузка. Когда свободной памяти нет, новые записи вытесняют старые по LRU.

memcached-tool 127.0.0.1:11211 display
memcached-tool 127.0.0.1:11211 stats
echo stats | nc -w 1 127.0.0.1 11211 | grep -E 'evictions|curr_items|bytes|limit_maxbytes'

evictions — главный сигнал, что памяти мало. Рост curr_items при постоянных промахах и вытеснениях означает, что рабочий набор не помещается в лимит. Увеличивайте -m или сокращайте TTL, но не рассчитывайте, что memcached сам сожмёт данные: серверного сжатия нет, его делают клиенты.

Максимальный размер одного объекта по умолчанию — 1 МиБ. Поднимать его через -I можно, но крупные значения хуже раскладываются по страницам и увеличивают задержки. Дробите большие структуры на несколько ключей.

Сеть и безопасность

У memcached нет аутентификации по умолчанию и нет шифрования трафика. Любой, кто дотянется до порта, прочитает и изменит кэш. Поэтому рабочий вариант — -l 127.0.0.1 на том же хосте или приватный интерфейс с фильтрацией по адресам приложений.

ss -lntup | grep memcached
grep -E '^-l|^-U|^-S' /etc/memcached.conf
sudo ufw allow from 10.0.0.0/24 to any port 11211 proto tcp
  • Не публикуйте 11211 в интернет и не пробрасывайте порт на роутере.
  • Оставьте UDP выключенным, если он не нужен: -U 0.
  • В многопользовательском окружении включайте SASL и отдельные учётные записи.
  • Проверяйте, кто слушает порт, после каждого обновления конфигурации.

Контейнеру хватит внутренней сети Docker без публикации порта.

Мониторинг и диагностика

Смотрите на долю промахов и динамику вытеснений. Hit rate ниже 80% на горячем кэше обычно означает неверные ключи или слишком короткий TTL; 99% при малом объёме данных может быть самообманом.

echo stats | nc -w 1 127.0.0.1 11211 | awk '$2=="get_hits"{h=$3} $2=="get_misses"{m=$3} END{printf "hit rate: %.1f%%\n", 100*h/(h+m)}'
watch -n 5 'memcached-tool 127.0.0.1:11211 stats'

Полезны listen_disabled_num и rejected_connections: они растут, когда исчерпан лимит соединений. Снимайте метрики раз в 10–15 секунд и храните историю: разовый снимок не отличит всплеск от постоянной деградации.

Чек-лист

  • Служба слушает только localhost или приватный адрес.
  • Лимит памяти задан и подтверждён через stats settings.
  • Приложение переживает недоступность кэша.
  • Настроены метрики hit rate, evictions и отказов соединений.

Итог

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

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте