Skip to content

Производительность: быстрые победы

Что даёт максимум ускорения за минимум усилий — в порядке отдачи. Глубокая теория: практики performance и инфраструктура.

Правило №0: сначала измерь

Оптимизация без замера — гадание. Инструменты по нарастающей:

ИнструментЧто показываетКогда
DebugbarЗапросы/тайминги текущей страницыDev, точечно
TelescopeЗапросы, джобы, кеш-промахи по всем запросамDev, постоянно
PulseМедленные запросы/джобы/endpoint'ы в продеProd, дешёвый мониторинг
EXPLAIN ANALYZE (postgres MCP)Почему конкретный запрос медленныйРазбор конкретного запроса
Benchmark::dd()Микрозамер куска кодаБыстрая проверка гипотезы

Симптом → куда смотреть:

СимптомПервый подозреваемый
Все страницы медленные одинаковоНет optimize/opcache; сессии/кеш не в Redis
Одна страница медленнаяN+1 или тяжёлый запрос без индекса (Debugbar)
Медленно под нагрузкой, быстро в одиночкуПул БД, блокировки, память PHP-FPM
Быстро гостю, медленно залогиненномуЗапросы в middleware/composer'ах на каждый запрос
Периодические провалыКрон/очередь конкурирует за ресурсы; кеш-стампид
Медленный первый запрос после деплояХолодный opcache/кеши — прогрев в деплое

Победа 1: кеши фреймворка (5 минут, всегда)

php artisan optimize в деплое = config + routes + views + events cache. Плюс composer install --optimize-autoloader --no-dev. Без этого прод исполняет сотни лишних stat() и парсит конфиги на каждый запрос. Opcache в PHP-образе — обязателен (по спецификации Docker).

Победа 2: убрать N+1 и лишние столбцы (час, кратный эффект)

  • ->with() на все связи, используемые в цикле/Blade — по стандарту N+1 это баг.
  • withCount()/withSum() вместо загрузки связей ради агрегата.
  • select() только нужного на списках: тащить content LONGTEXT в листинг из 50 карточек — типичный незаметный тормоз.
  • pluck()/value() там, где нужны не модели, а значения.

Победа 3: индексы под реальные запросы (час)

  • Индексировать столбцы из WHERE и ORDER BY реальных запросов (Pulse/pg_stat_statements покажут топ), составные — под частые комбинации.
  • Кириллический ILIKE не использует btree — functional index на lower().
  • Полнотекст на 1M+ строк — GIN + tsvector (готовый рецепт в миграциях).
  • Периодический аудит: неиспользуемые индексы замедляют запись — сносить.

Победа 4: кеш данных с гранулярной инвалидацией (полдня)

  • Тяжёлые выборки (меню, фасеты, блоки главной) — Cache::remember с тегами.
  • Инвалидация — в afterSave Filament по тегам, не Cache::flush() (стандарт студии).
  • Кеш-стампид на популярных ключах — Cache::flexible() (stale-while-revalidate).
  • Страничный кеш для гостей (spatie/laravel-responsecache или свой middleware) — самый дешёвый способ держать сотни RPS на контентном сайте.

Победа 5: вынести медленное из запроса (полдня)

Всё, что дольше ~100мс и не нужно для ответа — в очередь: почта, Telegram, ресайз изображений, внешние API, пересчёты. Лёгкая альтернатива для некритичного — defer(). HTTP-клиент — всегда с timeout, иначе чужие тормоза становятся вашими.

Победа 6: фронт и статика (час)

  • Ассеты Vite — с хешем в имени: отдавать с Cache-Control: immutable (Caddy-заголовок).
  • Изображения: WebP-конверсии medialibrary + loading="lazy" + размеры под вывод (не грузить 2000px в карточку 300px).
  • gzip/zstd на прокси — в студийном Caddy уже включено.
  • Критичный CSS инлайном, остальное — deferred, шрифты — font-display: swap.

Что НЕ делать преждевременно

СоблазнПочему рано
OctaneТиповому корпсайту не нужен; сначала кеши+индексы+N+1. Профит — на API с высоким RPS
Микросервисы/шардингМонолит на Laravel держит намного больше, чем кажется
Свой «умный» кеш-слойСложность > выгода; теги + remember закрывают 95%
Реплики БД для чтенияСначала pg_stat_statements: обычно виноваты 3–5 запросов
Переписать «медленный Eloquent» на raw SQLОбычно медленный не Eloquent, а отсутствие индекса/N+1

Бюджет производительности (ориентиры для типовых проектов студии)

МетрикаЦелевое
TTFB контентной страницы (с кешем)< 200 мс
TTFB без кеша< 500 мс
Запросов к БД на страницу< 15
Ответ API-endpoint'а< 300 мс
Обработка очереди уведомлений< 30 с от события

Превышение — не «оптимизировать всё», а найти виновника по таблице «симптом → подозреваемый».

Внутренняя база знаний студии