Кэширование в PHP: Redis, Memcached и встроенные решения — ускорение веб-приложений
Каждый разработчик PHP сталкивается с проблемой производительности: база данных отвечает медленно, страницы грузятся долго, а сервер задыхается под нагрузкой. Решение этих проблем лежит в грамотном кэшировании. В этой статье мы разберем три основных подхода к кэшированию в PHP: встроенные механизмы (APCu, OpCache), внешние серверы Memcached и Redis. Вы узнаете, когда какой инструмент выбрать, и получите готовые примеры кода для внедрения.
1. Встроенные решения: OpCache и APCu
Начнем с самого простого — кэширования на уровне PHP-движка. Здесь выделяют два ключевых компонента.
1.1. OpCache (PHP opcode cache)
OpCache встроен в PHP начиная с версии 5.5. Он кэширует скомпилированный байт-код (opcode) PHP-скриптов. Без него каждый запрос заново парсит и компилирует исходный код — это пустая трата ресурсов. Включение OpCache дает прирост скорости до 30-50% без единой строчки кода.
Настройка в php.ini:
opcache.enable=1
opcache.memory_consumption=128
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
1.2. APCu (Alternative PHP Cache user)
APCu — это кэш для пользовательских данных (переменных, результатов вычислений). Он работает в оперативной памяти и идеально подходит для хранения небольших, часто запрашиваемых данных: конфигураций, переводов, результатов простых SQL-запросов.
Пример работы с APCu:
<?php
// Сохраняем данные в кэш на 300 секунд
apcu_store('site_config', $config, 300);
// Получаем данные из кэша
$cached = apcu_fetch('site_config');
if ($cached === false) {
// Данных нет — загружаем и сохраняем
$cached = loadConfigFromDB();
apcu_store('site_config', $cached, 300);
}
// Удаляем ключ
apcu_delete('site_config');
?>
Плюсы: не требует внешних сервисов, прост в настройке, минимальная задержка. Минусы: работает только на одном сервере, данные теряются при перезапуске PHP-FPM.
2. Memcached — распределенный кэш простых данных
Memcached — это высокопроизводительная распределенная система кэширования в оперативной памяти. Она идеальна для хранения простых пар "ключ-значение" и сессий. В отличие от APCu, Memcached работает как отдельный сервер и может быть общим для нескольких веб-серверов.
Установка расширения: pecl install memcached
Пример подключения и работы:
<?php
$memcached = new Memcached();
$memcached->addServer('localhost', 11211);
// Установка с TTL (time to live) 600 секунд
$memcached->set('user_123_profile', $profileData, 600);
// Получение
$profile = $memcached->get('user_123_profile');
if ($memcached->getResultCode() === Memcached::RES_NOTFOUND) {
// Кэш промах — загружаем из БД
$profile = getUserProfileFromDB(123);
$memcached->set('user_123_profile', $profile, 600);
}
// Кэширование сессий (настройка в php.ini)
// session.save_handler = memcached
// session.save_path = "localhost:11211"
?>
Когда использовать Memcached:
- Кэширование результатов SQL-запросов (списки товаров, категории)
- Хранение сессий пользователей в распределенной среде
- Кэширование HTML-блоков (например, меню или подвал)
Ограничения: нет встроенных структур данных (только строки), максимальный размер значения 1 МБ (можно увеличить), нет персистентности — при перезапуске сервера кэш очищается.
3. Redis — продвинутый кэш с структурами данных
Redis — это не просто кэш, а полноценное хранилище данных в памяти с поддержкой сложных типов: списки, множества, хэши, сортированные множества. Он поддерживает персистентность (сохранение на диск), транзакции, pub/sub и TTL.
Установка через Composer (predis/predis) или pecl (phpredis):
Пример с использованием phpredis:
<?php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis->auth('password'); // если установлен пароль
// Хэши — идеально для объектов
$redis->hSet('user:456', 'name', 'Иван');
$redis->hSet('user:456', 'email', 'ivan@example.com');
$user = $redis->hGetAll('user:456');
// Списки — для очередей задач
$redis->lPush('task_queue', 'send_email_to_user_456');
$task = $redis->rPop('task_queue');
// Множества — для уникальных значений
$redis->sAdd('online_users', 'user_456');
$isOnline = $redis->sIsMember('online_users', 'user_456');
// Кэширование с TTL
$redis->setex('article:789', 3600, $articleContent);
// Инкремент — для счетчиков
$redis->incr('page_views:home');
?>
Продвинутый пример: кэширование с блокировкой (Mutex)
<?php
function getExpensiveData($redis, $key, $ttl = 600) {
$data = $redis->get($key);
if ($data !== false) {
return unserialize($data);
}
// Блокировка для предотвращения "cache stampede"
$lockKey = $key . ':lock';
if ($redis->setnx($lockKey, 1)) {
$redis->expire($lockKey, 10); // Таймаут блокировки
// Дорогостоящее вычисление
$data = computeExpensiveData();
$redis->setex($key, $ttl, serialize($data));
$redis->del($lockKey);
return $data;
}
// Если блокировка занята — ждем 50 мс и пробуем снова
usleep(50000);
return getExpensiveData($redis, $key, $ttl);
}
?>
Когда выбирать Redis:
- Сложные структуры