Страница со списком тормозит по мере роста. Как доказать, что причина — проблема лишних запросов N+1?
Нужно измерять, а не гадать. Включите лог запросов — Laravel Debugbar, Telescope или DB::listen() — и посчитайте запросы на страницу: N+1 виден как один и тот же SELECT, повторённый с меняющимся только id, и число повторов растёт с числом строк. Фикс подтверждайте повторным замером числа запросов, а не временем.
- ✗Рассуждать по одному лишь код-ревью вместо подсчёта запросов, которые шлёт реальный запрос
- ✗Искать в логе медленных запросов, где N быстрых запросов поодиночке не перешагивают порог
- ✗Объявлять победу по времени на dev-датасете, слишком маленьком, чтобы N что-то значил
- →Как ловить
N+1в CI до выката, а не уже в продакшене? - →Почему число запросов — лучший сигнал регрессии здесь, чем время обработки по часам?
Догадка — не диагноз. Включите лог запросов и посмотрите, что реально уходит в базу за одну загрузку страницы. N+1 имеет узнаваемую подпись: один и тот же SELECT с меняющимся только связанным id, и его повторов ровно столько, сколько строк в списке.
SELECT * FROM posts LIMIT 25 -- 1
SELECT * FROM users WHERE id = 4 -- 0.4 ms -- +1
SELECT * FROM users WHERE id = 9 -- 0.4 ms -- +1
SELECT * FROM users WHERE id = 12 -- 0.3 ms -- +1
... (25 раз)
Total: 26 queries, 11 ms
Ни один из этих запросов не попадёт в лог медленных запросов — каждый быстрый. Больно именно их количество. Поэтому метрика регрессии — число запросов на страницу, а не время: на dev-датасете из 5 строк время не покажет ничего, а счётчик покажет 6 вместо 2.
Инструменты: Laravel Debugbar и Telescope считают запросы за request; DB::listen() даёт то же самое без пакетов; Model::preventLazyLoading() в тестовом окружении превращает ленивую загрузку в исключение и ловит регрессию в CI. Проверять фикс нужно тем же счётчиком: 26 → 2.