• Czas czytania ~4 min
  • 11.10.2023

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.

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

O

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

O autorze CrazyBoy49z
WORK EXPERIENCE
Kontakt
Ukraine, Lutsk
+380979856297