Cara Menemukan Query Yang Lambat Pada Aplikasi Laravel - CRUDPRO

Cara Menemukan Query Yang Lambat Pada Aplikasi Laravel

Apakah situs web Anda lambat? Butuh waktu lama untuk memuat? Apakah pengguna Anda mengeluh bahwa itu hampir tidak dapat digunakan? Anda perlu memeriksa query database Anda. Dan saya akan menunjukkan cara yang bagus untuk dengan mudah membuat profil semua query basis data Anda.

Tentu saja, situs web Anda bisa lambat karena berbagai alasan, tetapi salah satu alasan paling umum adalah query basis data yang lambat.

Namun, Laravel tidak (sebagian besar waktu) menggunakan SQL untuk mengambil data dari database. Dengan query ORM dan Builder, mungkin sulit untuk mengidentifikasi query mana yang menyebabkan situs Anda menjadi sangat lambat.

Untungnya, Laravel memungkinkan Anda untuk menentukan callback yang akan dipanggil setiap kali query dijalankan. Untuk melakukannya, tambahkan kode berikut ke Service Provider mana pun (seperti AppServiceProvider).

public function boot()
{
    DB::listen(function ($query) {
        // TODO: make this useful
    });
}

Seperti yang Anda lihat, ia menerima variabel $query. Variabel ini adalah turunan dari kelas QueryExecuted. Ini berarti bahwa kami memiliki akses ke beberapa informasi tentang query yang dieksekusi.

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;
 });

Ini adalah informasi yang sangat berguna. Sekarang Anda memiliki cara untuk mengidentifikasi query lambat dengan melihat properti `$query->time`. Tetapi ini tidak memberi tahu saya di mana dalam kode query dieksekusi.

Bagaimana saya bisa mengetahui di mana query dieksekusi?

Variabel $query tidak memberikan informasi tentang asal $query, tetapi Anda dapat menggunakan fungsi bawaan PHP debug_backtrace() untuk mendapatkan informasi tersebut.

DB::listen(function ($query) {
    dd(debug_backtrace());
});

Ketika saya menjalankan ini di project saya, saya melihat yang berikut di browser saya:

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 [▶]
 ]
 ….

Ini adalah array yang berisi semua pemanggilan fungsi hingga titik ini dalam permintaan. Perhatikan hanya kunci "file" dan "baris" di setiap array.

Jika Anda melihat lebih dekat, Anda dapat melihat bahwa ada 63 pemanggilan fungsi dalam contoh saya. Ini mungkin cukup banyak. Ini adalah aplikasi sederhana, tetapi mungkin ada lebih banyak pemanggilan fungsi dalam aplikasi yang lebih kompleks. Lebih buruk lagi, jika Anda melihat yang paling atas, itu semua adalah fungsi internal dari framework Laravel. Haruskah saya memeriksa masing-masing sampai saya menemukan satu yang membantu?

temukan lokasi query

Seperti yang saya katakan sebelumnya, kebanyakan dari mereka adalah panggilan framework internal. Jadi sebagian besar file ini ada di direktori `vendor/`. Itu berarti Anda dapat memeriksa setiap "file" dan mengecualikan panggilan dengan "vendor/" seperti ini:

DB::listen(function ($query) {
    $stackTrace = collect(debug_backtrace())->filter(function ($trace) {
       return !str_contains($trace[‘file’], ‘vendor/’);
    });
 
    dd($stackTrace);
});

Di sini kita mengonversi array menjadi collection dan menggunakan metode filter. Jika file $trace saat ini memiliki vendor/ , hapus dari collection.

Ketika saya menjalankan kode di atas, saya mendapatkan yang berikut:

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
}

Ini adalah item yang cukup sedikit. *Berubah dari 63 menjadi hanya 5*. Dan bagian terbaiknya adalah item pertama dalam koleksi adalah lokasi persis yang memicu query SQL. Jadi, Anda dapat mengekstrak informasi itu untuk menemukan query paling lambat.

satukan semuanya

Sekarang setelah Anda memiliki semua informasi yang Anda butuhkan, mengapa tidak mencatatnya dan mencari query yang paling lambat?

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’]}
        — — — — — — 
        “);
    });
 }

Anda dapat menggunakan ini di aplikasi Anda untuk memeriksa file log. Informasi query yang mirip dengan berikut ini ditampilkan:

[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
 — — — — —

Sekarang setelah Anda mengetahui query mana yang paling lambat, mulailah mengerjakannya satu per satu dan cobalah untuk mempercepatnya, atau setidaknya menyimpannya dalam cache.

di luar debug

Meskipun ini sangat berguna untuk debugging, teknik ini dapat digunakan dalam banyak cara.

Anda dapat membuat laporan mingguan yang menampilkan query paling lambat dalam seminggu. Dapatkan alert Slack saat query melebihi batas waktu dan buat dasbor tempat Anda dan tim dapat melihat semua query dijalankan.

selamat mencoba, samapai jumpa lagi di artikel selanjutnya ya :)