Czy Twoja witryna działa wolno? Czy ładowanie trwa wieki? Czy użytkownicy skarżą się, że jest prawie bezużyteczny?. Należy sprawdzić zapytania do bazy danych. Pokażę wam zgrabny sposób łatwego profilowania wszystkich zapytań do bazy danych.
Oczywiście Twoja witryna może być powolna z wielu powodów, ale jednym z najczęstszych powodów są powolne zapytania do bazy danych.
Ale w laravel nie używamy SQL (przez większość czasu) do pobierania danych z naszej bazy danych, używamy elokwentnego ORM i kreatora zapytań laravel, co czasami utrudnia wskazanie zapytania, które sprawia, że nasza strona jest tak wolna.
DB::listen()
Na szczęście w laravel możemy zdefiniować wywołanie zwrotne, które jest wywoływane za każdym razem, gdy wykonywane jest zapytanie (patrz tutaj). Aby to zrobić, dodaj następujący kod do dowolnego dostawcy usług (na przykład AppServiceProvider):
public function boot()
{
DB::listen(function ($query) {
// TODO: make this useful
});
}
Jak widać, otrzymujemy zmienną $query, ta zmienna jest instancją klasy QueryExecuted. Oznacza to, że mamy dostęp do niektórych informacji o zapytaniu, które zostało wykonane:
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;
});
Jest to bardzo przydatna informacja, teraz mamy sposób na identyfikację powolnych zapytań, patrząc na $query->time
właściwość. Ale to nie mówi nam, gdzie zapytanie zostało wykonane w naszym kodzie.
Skąd mam wiedzieć, gdzie zostało wykonane zapytanie?
Mimo że zmienna $query
nie daje nam żadnych informacji o tym, skąd pochodzi, $query
nadal możemy uzyskać te informacje za pomocą wbudowanej funkcji debug_backtrace()
PHP .
DB::listen(function ($query) {
dd(debug_backtrace());
});
Jeśli uruchomisz to w swoim projekcie, zobaczysz coś takiego w przeglądarce:
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 [▶]
]
....
Jest to tablica zawierająca wszystkie wywołania funkcji do tego momentu na żądanie. Skupię się tylko na file
i line
w każdej tablicy.
Jeśli przyjrzysz się uważnie, zobaczysz, że w moim przykładzie były 63 wywołania funkcji, które można uznać za dużo, a to jest prosta aplikacja, w bardziej skomplikowanej, może być o wiele więcej. Co gorsza, jeśli spojrzysz na te na górze, wszystkie są wewnętrznymi funkcjami struktury laravel. Czy mamy patrzeć na każdą z nich, dopóki nie znajdziemy czegoś, co może nam pomóc?
Znajdowanie lokalizacji
zapytania Jak powiedziałem wcześniej, większość z nich to wewnętrzne wywołania frameworka, co oznacza, że większość z tych plików znajduje się w naszym vendor/
katalogu. Oznacza to, że możemy sprawdzić każde i odfiltrować każde file
wywołanie, które ma , na przykład:
DB::listen(function ($query) {
$stackTrace = collect(debug_backtrace())->filter(function ($trace) {
return !str_contains($trace['file'], 'vendor/');
});
dd($stackTrace);
});
Tutaj konwertuję tablicę na kolekcję, aby użyć filter
metody, jeśli file
prąd $trace
mavendor/
vendor/
, usuwamy go z kolekcji.
Jeśli uruchomisz powyższy kod, zobaczysz coś takiego:
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
}
To jest o wiele mniej przedmiotów, przeszliśmy z 63 do tylko 5. A najlepsze jest to, że pierwszy element w kolekcji jest dokładnym miejscem, w którym uruchomiliśmy zapytanie SQL. Oznacza to, że możemy wyodrębnić te informacje, aby znaleźć najwolniejsze zapytania.
Składanie wszystkiego w całość
Teraz, gdy mamy już wszystkie potrzebne informacje, dlaczego nie zarejestrujemy ich, abyśmy mogli sprawdzić i wyszukać najwolniejsze zapytania?:Jeśli używasz tego w swojej aplikacji, możesz sprawdzić plik dziennika i powinieneś zobaczyć informacje o zapytaniach w następujący sposób:
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
----------
Teraz wiesz, które zapytania są najwolniejsze i zacznij je rozwiązywać jeden po drugim, Postaraj się, aby były szybsze lub przynajmniej buforuj je.
Poza debugowaniem
: Jest to bardzo przydatne do debugowania, ale ta technika może być używana na wiele sposobów.
Możesz utworzyć cotygodniowy raport pokazujący najwolniejsze zapytania w tygodniu.
Możesz otrzymać alert slack, jeśli zapytanie przekroczy próg
czasuMożesz utworzyć pulpit nawigacyjny, na którym Ty i Twój zespół możecie zobaczyć każde wykonane
zapytanieNiebo jest granicą.
Ostatnia aktualizacja 1 rok temu.