Кэширование и производительность
Драйверы кэша, Cache API, оптимизация запросов, Octane, Redis.
Junior Какие драйверы кэша есть в Laravel и как работать с Cache?
Драйверы (CACHE_STORE в .env): file, database, redis, memcached, dynamodb, array (в памяти запроса, для тестов).
Cache::put('key', $value, 600); // TTL в секундах
Cache::put('key', $value, now()->addHour());
Cache::get('key', 'default');
Cache::remember('stats', 3600, fn () => $this->calcStats()); // взять или вычислить
Cache::rememberForever('key', fn () => ...);
Cache::forever('key', $value);
Cache::forget('key');
Cache::flush(); // всё хранилище (осторожно!)
Cache::has('key'); Cache::pull('key'); // get + forget
Cache::increment('counter'); Cache::add('key', $v, 60); // только если нет
Теги (redis/memcached): Cache::tags(['users', $id])->put(...), сброс Cache::tags('users')->flush().
Atomic locks (redis):
Cache::lock('import', 10)->get(function () { /* только один процесс */ });
Правило: remember + осмысленные ключи + инвалидация при изменении данных (через события моделей/observer).
Middle Какие команды оптимизации запускают при деплое?
php artisan config:cache # все конфиги → один файл, .env больше не читается
php artisan route:cache # маршруты сериализованы (нельзя замыкания!)
php artisan view:cache # прекомпиляция Blade
php artisan event:cache # карта событий/слушателей
php artisan optimize # всё сразу
php artisan optimize:clear # сброс всех кэшей
Дополнительно:
composer install --no-dev --optimize-autoloader— classmap-автолоад;- OPcache в php.ini (проверить
opcache.enable=1,validate_timestamps=0на проде); - миграции:
php artisan migrate --force; - перезапуск воркеров:
queue:restart(долгоживущие процессы держат старый код).
Частый вопрос-ловушка: после config:cache хелпер env() возвращает null вне конфигов — поэтому env() вызывают только в config/*.php.
Middle Как устроено кэширование HTTP-ответов и сессий? Что такое Redis в стеке Laravel?
Redis — центральный компонент: кэш, сессии, очереди, блокировки, rate limiter, broadcasting:
'connections' => ['cache' => [...], 'default' => [...]] // config/database.php
Рекомендуемый клиент — phpredis (C-расширение, быстрее predis). Разделяйте логические БД или префиксы для cache/queue/session.
Сессии (SESSION_DRIVER): file (по умолчанию), cookie (шифрованные, всё на клиенте), database, redis. API — session(['key' => $v]), $request->session()->get(...). Flash-данные — на один следующий запрос.
HTTP-кэширование:
return response($html)->header('Cache-Control', 'public, max-age=300');
// ETag/Last-Modified + 304 Not Modified
Middleware cache.headers. Для публичных страниц — полностраничный кэш (reverse proxy: nginx, Varnish, CDN) эффективнее любого PHP-кэша.
Балансировка: сессии в Redis/БД обязательны, если приложение за несколькими серверами (sticky sessions — костыль).
Senior Что такое Laravel Octane и как он меняет модель выполнения?
Octane запускает Laravel под высокопроизводительными серверами приложений (Swoole, RoadRunner, FrankenPHP): приложение загружается в память один раз и обслуживает тысячи запросов без повторного bootstrap'а. Рост RPS — в разы.
php artisan octane:start --server=roadrunner --workers=4
Главная ловушка — утечка состояния между запросами: приложение долгоживущее, поэтому:
- нельзя хранить данные запроса в статических свойствах и свойствах синглтонов — они «переедут» в следующий запрос;
- осторожно с
app()->instance()и глобальным состоянием; - контейнер «прогревается» заново на каждый запрос, но синглтоны из bootstrap остаются;
- утечки памяти теперь накапливаются — есть
octane:reloadи--max-requests.
Взамен: конкурентные задачи (Octane::concurrently), таблицы в shared memory (Swoole Table), тики.
Octane не нужен мелким сайтам; критичен для highload API.
Senior Как искать и устранять медленные запросы и «узкие места» приложения?
Диагностика:
- Laravel Telescope (dev) / Debugbar — запросы, время, N+1;
DB::listen(fn ($q) => Log::info($q->sql, [$q->time]))илиDB::enableQueryLog();- slow query log СУБД +
EXPLAINдля подозрительных запросов; - APM на проде: New Relic, Sentry Performance, Horizon для очередей.
Типовые проблемы и решения:
| Проблема | Решение |
|---|---|
| N+1 | with(), preventLazyLoading() |
SELECT * |
select(['id','name']), pluck |
| Отсутствие индексов | индексы под where/join/order by; составные — порядок важен |
Тяжёлый COUNT при пагинации |
simplePaginate / cursorPaginate |
| Загрузка тысяч записей | chunk, cursor, lazy |
| Дорогие вычисления в запросе | кэш remember, денормализация, материализованные данные |
| Внешние HTTP в запросе | очереди, Http::pool() |
Системный уровень: Redis-кэш, OPcache, Octane, CDN для статики, HTTP-кэширование (Cache-Control, ETag).