Спершу треба побачити самі запити, а не здогадуватися.
Подивитися, що виконується:
// AppServiceProvider::boot()
DB::listen(function ($query) {
if ($query->time > 100) {
Log::warning('Slow query', ['sql' => $query->sql, 'ms' => $query->time]);
}
});
Разово по одному запиту допомагає ->toSql() і ->dd(). У розробці - Telescope, Debugbar, Clockwork.
Прочитати план виконання. Це головний інструмент, бо він показує, чи використано індекс:
Post::where('is_published', true)->orderByDesc('published_at')->explain()->dd();
У PostgreSQL шукайте Seq Scan на великій таблиці - це повний перебір; у MySQL - type: ALL і rows, близьке до розміру таблиці.
Найчастіші причини:
- Немає індексу на колонці з
whereчиorder by. Для складених умов порядок колонок в індексі має значення: індекс(is_published, published_at)працює для фільтра заis_publishedіз сортуванням, а(published_at, is_published)- ні. - Функція на колонці вбиває індекс:
whereRaw('lower(email) = ?')змусить перебір, поки немає функціонального індексу. LIKE '%текст%'індексом не користується - для пошуку потрібен повнотекстовий індекс або окремий рушій.OFFSETна глибоких сторінках:offset 100000змушує базу відкинути сто тисяч рядків. Рятує курсорна пагінація -cursorPaginate().
Індекс не безкоштовний: він сповільнює запис і займає місце. Додавати варто за фактом виміряної проблеми, а не «на всяк випадок» на кожну колонку.