Введение: Почему кэширование — основа быстрого PHP-приложения
Каждый разработчик PHP рано или поздно сталкивается с проблемой производительности. Когда база данных начинает тормозить, а страницы грузятся по 3-5 секунд, первое, что приходит на ум — кэширование. Это не просто "сохранить данные куда-то", это целая философия оптимизации.
В этой статье мы разберём все популярные подходы к кэшированию в PHP: от простого файлового кэша до промышленных решений на Redis и Memcached. Вы узнаете, как выбрать правильный инструмент и избежать типичных ошибок. Поехали!
1. Встроенные механизмы кэширования в PHP
1.1. Кэширование опкода: OPcache
Прежде чем говорить о внешних хранилищах, стоит вспомнить, что PHP сам умеет кэшировать свой код. Механизм OPcache сохраняет скомпилированные скрипты в оперативной памяти, избавляя PHP от необходимости каждый раз парсить и компилировать файлы.
Включение OPcache в php.ini:
opcache.enable=1
opcache.memory_consumption=128
opcache.max_accelerated_files=10000
opcache.revalidate_freq=2
Это первая линия обороны. Без OPcache любое другое кэширование будет менее эффективно.
1.2. APCu — пользовательский кэш в памяти
APCu (Alternative PHP Cache) — это расширение для хранения произвольных данных в общей памяти процесса PHP. Идеально подходит для небольших проектов, где не требуется распределённое кэширование.
Пример работы с APCu:
<?php
// Сохраняем данные на 5 минут
apcu_store('my_key', ['data' => 'value'], 300);
// Читаем данные
$data = apcu_fetch('my_key');
if ($data === false) {
echo "Кэш не найден, генерируем заново";
}
// Удаляем ключ
apcu_delete('my_key');
?>
Плюсы: молниеносная скорость, простота. Минусы: данные живут только в рамках одного сервера.
2. Memcached: простота и скорость
2.1. Что такое Memcached и зачем он нужен
Memcached — это распределённая система кэширования в оперативной памяти. Она была создана для ускорения динамических веб-приложений путём снижения нагрузки на базы данных. В отличие от APCu, Memcached работает как отдельный сервер, к которому можно подключаться с разных инстансов PHP.
2.2. Установка и подключение Memcached
Сначала установите сервер Memcached:
sudo apt install memcached
sudo systemctl start memcached
Затем установите PHP-расширение:
sudo pecl install memcached
Теперь можно работать с кэшем:
<?php
$memcached = new Memcached();
$memcached->addServer('localhost', 11211);
// Сохраняем на 10 минут
$memcached->set('user_123', ['name' => 'Иван', 'email' => 'ivan@example.com'], 600);
// Читаем
$user = $memcached->get('user_123');
if ($user === false) {
// Загружаем из БД
}
?>
2.3. Когда выбирать Memcached
- Когда нужно простое key-value хранилище
- Когда данные имеют короткий срок жизни (сессии, временные данные)
- Когда не требуется персистентность (Memcached не сохраняет данные на диск)
3. Redis: мощный инструмент для сложных сценариев
3.1. Почему Redis лучше Memcached?
Redis — это не просто кэш, это полноценная in-memory база данных с поддержкой сложных структур данных: списки, множества, хэши, битовые карты. В отличие от Memcached, Redis поддерживает:
- Персистентность — данные не пропадают при перезагрузке
- Транзакции и Lua-скрипты
- Pub/Sub для обмена сообщениями
- Репликацию и кластеризацию
3.2. Примеры работы с Redis в PHP
Для работы с Redis используйте библиотеку predis/predis (composer) или расширение phpredis.
Установка через Composer:
composer require predis/predis
Базовый пример:
<?php
require 'vendor/autoload.php';
$client = new Predis\Client([
'scheme' => 'tcp',
'host' => '127.0.0.1',
'port' => 6379,
]);
// Сохраняем данные с TTL 1 час
$client->setex('user:456', 3600, json_encode(['name' => 'Мария']));
// Читаем
$user = $client->get('user:456');
if ($user) {
$user = json_decode($user, true);
}
// Инкремент счётчика (популярно для ограничения запросов)
$client->incr('page_views');
$client->expire('page_views', 86400); // сброс через сутки
?>
3.3. Кэширование сложных запросов с Redis
Реальный пример: кэширование результатов выборки из БД с автоматическим обновлением:
<?php
function getArticles($categoryId) {
global $client, $pdo;
$cacheKey = "articles:category:$categoryId";
// Пытаемся достать из Redis
$cached = $client->get($cacheKey);
if ($cached) {
return json_decode($cached, true);
}
// Если нет в кэше — идём в БД
$stmt = $pdo->prepare("SELECT * FROM articles WHERE category_id = ?");
$stmt->execute([$categoryId]);
$articles = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Сохраняем в кэш на 15 минут
$client->setex($cacheKey, 900, json_encode($articles));
return $articles;
}
?>