Охота на N+1 и устранение лишних запросов в Laravel

Работа над производительностью часто бывает скучной.

Это не всегда истории о героях, которые снижают задержку на 80%. Чаще всего это просто наведение порядка. Вы находите запросы, о существовании которых даже не подозревали. А затем удаляете их.

Если ваше Laravel-приложение растет, вы, скорее всего, сталкиваетесь с этими тремя проблемами.

  1. Скрытые N+1 запросы

Нельзя исправить то, чего не видишь. Используйте такие инструменты, как beyondcode/laravel-query-detector, в вашей локальной среде разработки. Он подскажет вам, когда вы используете паттерн «цикл + ленивая загрузка» (loop-and-lazy-load).

Не запускайте это в продакшене. Это создает накладные расходы. Регистрируйте инструмент только тогда, когда приложение находится в режиме local или testing.

  1. Избыточная жадная загрузка (Eager Loading)

Часто проблемы N+1 пытаются решить добавлением with(). Но это создает новую проблему. Вы можете начать жадную загрузку связи, которая больше не используется в вашем представлении (view).

Если в результате редизайна из таблицы удаляется колонка, вы должны удалить соответствующий вызов with() в контроллере. Вы платите за данные, которые просто выбрасываете.

Относитесь к каждой жадной загрузке как к запросу на получение данных. Если данные не используются, уберите этот запрос.

  1. Повторное вычисление одних и тех же данных

Некоторые значения остаются неизменными на протяжении всего запроса. Если вы вычисляете их несколько раз, вы тратите ресурсы CPU впустую. Чтобы это исправить, используйте мемоизацию.

Пример:

protected ?string $defaultConnection = null;

public function getDefaultConnectionName(): string
{
    return $this->defaultConnection ??= $this->resolveDefaultConnection();
}

Оператор ??= вычисляет значение один раз и использует его повторно до конца запроса.

  1. Ловушки запросов в дашбордах

Дашборды часто выполняют отдельный запрос count() для каждой карточки. Если у вас шесть карточек, вы выполняете шесть запросов.

Вместо этого сделайте следующее:

  • Группируйте подсчеты. Получайте все итоги по одной таблице одним запросом.
  • Используйте короткое кэширование. Статистика на дашбордах не обязательно должна быть актуальной до секунды. Кэшируйте её на пять минут.

Один посетитель оплачивает стоимость запроса. Все остальные получают результат из кэша.

Финальный совет: Предотвращайте регрессии

Оптимизация бесполезна, если разработчики позже снова добавят плохой код. Используйте Pest, чтобы проверять (assert) количество запросов. Установите лимит на то, сколько запросов может выполнять страница. Если разработчик добавит N+1, тест провалится.

Оптимизация — это в основном процесс удаления. Удаляйте неиспользуемые загрузки. Удаляйте повторно вычисляемые значения. Удаляйте ненужные запросы.

Source: https://dev.to/nasrulhazim/a-day-of-performance-hardening-hunting-n1s-and-killing-wasted-queries-in-laravel-568p