Ваш веб-сайт повільний? Чи потрібно багато часу, щоб завантажитися? Користувачі скаржаться, що він майже непридатний для використання?. Вам слід перевірити запити до бази даних. І я покажу вам акуратний спосіб легкого профілювання всіх запитів до бази даних.
Звичайно, ваш веб-сайт може працювати повільно з багатьох причин, але однією з найпоширеніших причин є повільні запити до бази даних.
Але в laravel ми не використовуємо SQL (більшу частину часу) для отримання даних з нашої бази даних, ми використовуємо красномовний конструктор ORM і запитів laravel, який іноді ускладнює визначення того запиту, який робить наш сайт таким повільним.
DB::listen()
На щастя, в laravel ми можемо визначити зворотний виклик, який викликається кожного разу, коли виконується запит (див. тут). Для цього додайте наступний код до будь-якого постачальника послуг (наприклад, AppServiceProvider):
public function boot()
{
DB::listen(function ($query) {
// TODO: make this useful
});
}
Як бачите, ми отримуємо змінну $query, ця змінна є екземпляром класу QueryExecuted. Це означає, що у нас є доступ до деякої інформації про запит, який був виконаний:
DB::listen(function ($query) {
$query->sql; // the sql string that was executed
$query->bindings; // the parameters passed to the sql query (this replace the '?'s in the sql string)
$query->time; // the time it took for the query to execute;
});
Це дуже корисна інформація, тепер у нас є спосіб ідентифікувати повільні запити, подивившись на $query->time
властивість. Але це не говорить нам, де був виконаний запит у нашому коді.
Як дізнатися, де був виконаний запит?
Незважаючи на те, що змінна $query
не дає нам жодної інформації про те, звідки вона $query
взялася, ми все одно можемо отримати цю інформацію за допомогою вбудованої функції debug_backtrace()
PHP .
DB::listen(function ($query) {
dd(debug_backtrace());
});
Якщо ви запустите це у своєму проекті, ви побачите щось на кшталт цього у браузері:
array:63 [▼
0 => array:7 [▼
"file" => "/home/cosme/Documents/projects/cosme.dev/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php"
"line" => 404
"function" => "App\Providers\{closure}"
"class" => "App\Providers\AppServiceProvider"
"object" => App\Providers\AppServiceProvider {#140 ▶}
"type" => "->"
"args" => array:1 [▶]
]
1 => array:7 [▼
"file" => "/home/cosme/Documents/projects/cosme.dev/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php"
"line" => 249
"function" => "Illuminate\Events\{closure}"
"class" => "Illuminate\Events\Dispatcher"
"object" => Illuminate\Events\Dispatcher {#27 ▶}
"type" => "->"
"args" => array:2 [▶]
]
2 => array:7 [▼
"file" => "/home/cosme/Documents/projects/cosme.dev/vendor/laravel/framework/src/Illuminate/Database/Connection.php"
"line" => 887
"function" => "dispatch"
"class" => "Illuminate\Events\Dispatcher"
"object" => Illuminate\Events\Dispatcher {#27 ▶}
"type" => "->"
"args" => array:1 [▶]
]
....
Це масив, що містить усі виклики функцій до цього моменту у запиті. Я збираюся зосередитися тільки на ключах і file
line
в кожному масиві.
Якщо придивитися, то можна побачити, що в моєму прикладі було 63 виклики функцій, що можна вважати багато, і це просте додаток, в більш складному може бути набагато більше. Що ще гірше, якщо ви подивитеся на ті, що знаходяться вгорі, всі вони є внутрішніми функціями фреймворку laravel. Чи повинні ми дивитися на кожну з них, поки не знайдемо щось, що може нам допомогти?
Пошук розташування
запиту Як я вже говорив раніше, більшість з них є внутрішніми викликами фреймворків, а це означає, що більшість цих файлів знаходяться в нашому vendor/
каталозі. Це означає, що ми можемо перевірити кожен file
і відфільтрувати будь-який виклик, який має , наприклад:
DB::listen(function ($query) {
$stackTrace = collect(debug_backtrace())->filter(function ($trace) {
return !str_contains($trace['file'], 'vendor/');
});
dd($stackTrace);
});
Тут я перетворюю масив на колекцію, щоб використовувати filter
метод, якщо file
поточний $trace
має vendor/
vendor/
ми видаляємо його з колекції.
Якщо ви запустите код вище, ви побачите щось на кшталт цього:
Illuminate\Support\Collection {#1237 ▼
#items: array:5 [▼
12 => array:7 [▼
"file" => "/home/cosme/Documents/projects/cosme.dev/app/Models/Post.php"
"line" => 61
"function" => "get"
"class" => "Illuminate\Database\Eloquent\Builder"
"object" => Illuminate\Database\Eloquent\Builder {#310 ▶}
"type" => "->"
"args" => []
]
16 => array:6 [▶]
17 => array:6 [▶]
61 => array:7 [▶]
62 => array:4 [▶]
]
#escapeWhenCastingToString: false
}
Це набагато менше елементів, ми перейшли з 63 до лише 5. І найприємніше те, що перший елемент у колекції — це саме те місце, де ми запустили SQL-запит. Це означає, що ми можемо витягти цю інформацію, щоб знайти найповільніші запити.
Збираємо все
разом Тепер, коли у нас є вся необхідна інформація, чому б нам не зареєструвати її, щоб ми могли перевіряти та шукати найповільніші запити?:Якщо ви використовуєте це у своєму додатку, ви можете перевірити свій файл журналу, і ви повинні побачити інформацію про запити наступним чином:
public function boot()
{
DB::listen(function ($query) {
$location = collect(debug_backtrace())->filter(function ($trace) {
return !str_contains($trace['file'], 'vendor/');
})->first(); // grab the first element of non vendor/ calls
$bindings = implode(", ", $query->bindings); // format the bindings as string
Log::info("
------------
Sql: $query->sql
Bindings: $bindings
Time: $query->time
File: ${location['file']}
Line: ${location['line']}
------------
");
});
}
[2022-02-03 02:20:14] local.INFO:
------------
Sql: select "title", "slug", "body" from "posts" where "published" = ? order by "id" desc
Bindings: 1
Time: 0.18
File: /home/cosme/Documents/projects/cosme.dev/app/Models/Post.php
Line: 61
----------
Тепер ви знаєте, які запити є найповільнішими, і починаєте обробляти їх один за одним, Спробуйте зробити їх швидшими або, принаймні, кешувати.
Поза налагодженням
Це дуже корисно для налагодження, але цю техніку можна використовувати багатьма способами.
Ви можете створити щотижневий звіт із найповільнішими запитами за тиждень.
Ви можете отримати попередження про слабину, якщо запит перевищує поріг
часуВи можете створити інформаційну панель, де ви та ваша команда зможете бачити кожен виконаний
запитНебо – це межа.
Останнє оновлення: 1 рік тому.