Как ускорить Laravel приложение: кэширование через Redis или Memcached
Недавно я столкнулся с проблемой, знакомой многим разработчикам: приложение на Laravel, которое отлично работало на этапе разработки, начало заметно тормозить под реальной нагрузкой. Time to First Byte вырос до 800-900 мс, база данных захлёбывалась от повторяющихся запросов, а клиент начал задавать неприятные вопросы про «жуткие тормоза». В этой статье расскажу, как я решил эту проблему с помощью кэширования через Redis, и поделюсь конкретными кейсами с кодом.
Почему я выбрал Redis, а не Memcached
Прежде чем идти дальше, объясню свой выбор. Я тестировал оба решения на одном проекте и вот к каким выводам пришёл:
Redis выиграл для меня по нескольким причинам:
- Поддержка сложных структур данных (списки, множества, хэши) — очень важно для тегированного кэша.
- Persistence на диск — при перезапуске сервера кэш не теряется полностью.
- Встроенные механизмы pub/sub, которые я использовал для инвалидации кэша между несколькими серверами.
- Нативная интеграция с Laravel через пакет predis/predis или phpredis.
Memcached проще и для некоторых задач чуть быстрее, например для простых key-value операций, но для проекта с большим количеством связанных данных Redis показал себя практичнее.
Шаг 1: Настройка окружения
Первым делом устанавливаем PHP-расширение phpredis — оно быстрее чем predis за счёт того что написано на C:
sudo apt-get install php-redis
Или через composer, если все же предпочитаете predis:
composer require predis/predis
В .env файле настраиваем подключение:
CACHE_DRIVER=redis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
REDIS_CLIENT=phpredis
В config/database.php проверяем секцию redis:
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis'),
'default' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_DB', 0),
],
],
Шаг 2: Первая победа — кэширование тяжёлых запросов
В моём проекте была страница каталога товаров с фильтрами, которая генерировала запрос на 200+ строк SQL с несколькими джойнами. Каждый запрос занимал 150-200 мс. Вот как я это исправил:
public function getProductsByCategory($categoryId, $filters = [])
{
$cacheKey = 'products.category.' . $categoryId . '.' . md5(serialize($filters));
return Cache::remember($cacheKey, now()->addMinutes(30), function () use ($categoryId, $filters) {
return Product::