Настройка Memcached для кэширования веб-приложения

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Настройка Memcached для кэширования веб-приложения
Средняя
от 1 рабочего дня до 3 рабочих дней
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1214
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    852
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    823
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    815

Настройка Memcached для кэширования веб-приложения

Memcached — это распределённый кэш в памяти с предельно простой моделью: ключ, значение, TTL. Никаких транзакций, персистентности, pub/sub. Именно эта простота делает его быстрее Redis в сценариях read-heavy кэширования — задержка при попадании в кэш составляет 0.1–0.5 мс против 1–3 мс у Redis с AOF-персистентностью.

Используется там, где нужно кэшировать большие объёмы однородных данных: результаты SQL-запросов, сериализованные объекты, HTML-фрагменты, API-ответы.

Установка и базовая конфигурация

# Ubuntu/Debian
apt install memcached libmemcached-dev

# Редактируем /etc/memcached.conf
-d                          # daemon mode
-m 2048                     # 2GB RAM
-p 11211                    # порт
-u memcache                 # пользователь
-l 127.0.0.1               # только localhost (не выставлять наружу!)
-c 2048                     # max connections
-t 8                        # threads (= кол-во CPU ядер)
-I 10m                      # max item size (дефолт 1MB, увеличиваем до 10MB)
-o modern                   # современные опции slab allocator

Перезапуск и проверка:

systemctl restart memcached
echo "stats" | nc 127.0.0.1 11211 | grep -E "curr_items|bytes|hit_rate|evictions"

PHP-интеграция через php-memcached

pecl install memcached
echo "extension=memcached.so" > /etc/php/8.2/mods-available/memcached.ini
phpenmod memcached

Базовое использование:

$mc = new Memcached();
$mc->addServer('127.0.0.1', 11211);

// Настройки клиента
$mc->setOptions([
    Memcached::OPT_CONNECT_TIMEOUT    => 50,    // ms
    Memcached::OPT_RETRY_TIMEOUT      => 300,
    Memcached::OPT_SEND_TIMEOUT       => 100,
    Memcached::OPT_RECV_TIMEOUT       => 100,
    Memcached::OPT_POLL_TIMEOUT       => 100,
    Memcached::OPT_COMPRESSION        => true,
    Memcached::OPT_SERIALIZER         => Memcached::SERIALIZER_IGBINARY,
    Memcached::OPT_TCP_NODELAY        => true,
    Memcached::OPT_NO_BLOCK           => true,   // async I/O
]);

Кэширование SQL-запросов

Паттерн cache-aside — самый распространённый:

class ProductRepository
{
    private Memcached $cache;
    private PDO $db;
    private int $defaultTtl = 300; // 5 минут

    public function findById(int $id): ?array
    {
        $key = "product:v2:{$id}";

        $product = $this->cache->get($key);
        if ($this->cache->getResultCode() === Memcached::RES_SUCCESS) {
            return $product;
        }

        $stmt = $this->db->prepare('SELECT * FROM products WHERE id = ? AND active = 1');
        $stmt->execute([$id]);
        $product = $stmt->fetch(PDO::FETCH_ASSOC) ?: null;

        if ($product !== null) {
            $this->cache->set($key, $product, $this->defaultTtl);
        }

        return $product;
    }

    public function findByCategoryWithPagination(int $categoryId, int $page, int $perPage): array
    {
        $offset = ($page - 1) * $perPage;
        $key = "products:cat:{$categoryId}:p{$page}:pp{$perPage}";

        $result = $this->cache->get($key);
        if ($this->cache->getResultCode() === Memcached::RES_SUCCESS) {
            return $result;
        }

        $stmt = $this->db->prepare('
            SELECT p.*, c.name as category_name
            FROM products p
            JOIN categories c ON c.id = p.category_id
            WHERE p.category_id = ? AND p.active = 1
            ORDER BY p.created_at DESC
            LIMIT ? OFFSET ?
        ');
        $stmt->execute([$categoryId, $perPage, $offset]);
        $result = [
            'items' => $stmt->fetchAll(PDO::FETCH_ASSOC),
            'page'  => $page,
        ];

        $this->cache->set($key, $result, 120);
        return $result;
    }

    public function invalidateProduct(int $id): void
    {
        $this->cache->delete("product:v2:{$id}");
        // Инвалидация пагинации по категории — через паттерн тегов
    }
}

Инвалидация через теги (эмуляция)

Memcached не поддерживает теги нативно. Стандартный приём — версионированные namespace:

class CacheTagManager
{
    private Memcached $mc;

    public function getTagVersion(string $tag): int
    {
        $version = $this->mc->get("tag_version:{$tag}");
        if ($this->mc->getResultCode() !== Memcached::RES_SUCCESS) {
            $version = time();
            $this->mc->set("tag_version:{$tag}", $version, 0); // no expiry
        }
        return (int)$version;
    }

    public function buildKey(string $base, array $tags): string
    {
        $versions = array_map(
            fn($tag) => $this->getTagVersion($tag),
            $tags
        );
        return $base . ':' . implode(':', $versions);
    }

    public function invalidateTag(string $tag): bool
    {
        return $this->mc->increment("tag_version:{$tag}", 1, time()) !== false;
    }
}

// Использование
$tagManager = new CacheTagManager($mc);

// Ключ зависит от версии тега категории
$key = $tagManager->buildKey("products:cat:5:p1", ['category:5', 'products']);
$data = $mc->get($key);

// При изменении категории — все зависимые кэши становятся "несуществующими"
$tagManager->invalidateTag('category:5');

Распределённый кэш — consistent hashing

При нескольких серверах критично использовать consistent hashing, чтобы при добавлении/удалении узла инвалидировалось минимум ключей:

$mc = new Memcached('persistent_pool'); // persistent connection pool
$mc->addServers([
    ['memcached-1.internal', 11211, 40], // weight 40
    ['memcached-2.internal', 11211, 40],
    ['memcached-3.internal', 11211, 20], // меньший вес — меньше трафика
]);
$mc->setOption(Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_CONSISTENT);
$mc->setOption(Memcached::OPT_LIBKETAMA_COMPATIBLE, true);
$mc->setOption(Memcached::OPT_REMOVE_FAILED_SERVERS, true);
$mc->setOption(Memcached::OPT_SERVER_FAILURE_LIMIT, 3);
$mc->setOption(Memcached::OPT_RETRY_TIMEOUT, 2);

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

# Статистика сервера
echo "stats" | nc 127.0.0.1 11211

# Важные метрики:
# get_hits / (get_hits + get_misses) = hit rate (цель > 90%)
# evictions > 0 = не хватает памяти, нужно увеличить -m
# curr_connections — текущие соединения

# Смотрим все ключи (только для дебага, не на проде)
echo "stats cachedump 1 100" | nc 127.0.0.1 11211

Prometheus + memcached_exporter:

docker run -d --name memcached-exporter \
  -p 9150:9150 \
  prom/memcached-exporter:latest \
  --memcached.address=127.0.0.1:11211

Grafana dashboard id: 7603 — готовый дашборд для Memcached.

Типичный таймлайн

День 1 — установка, конфигурация размера памяти и thread count, настройка firewall (порт 11211 должен быть закрыт снаружи).

День 2 — интеграция с приложением, реализация cache-aside для тяжёлых SQL-запросов, инвалидация при записи.

День 3 — проверка hit rate, настройка мониторинга, тюнинг TTL по типам данных. Если hit rate ниже 80% — анализ промахов и исправление стратегии кэширования.