Кэш в памяти — самый дешёвый способ снять с базы данных повторяющиеся запросы. 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 и метрик — и только потом увеличивайте память и усложняйте схему ключей.