Ваш сайт работает медленно? Загружается ли целая вечность? Пользователи жалуются, что он почти непригоден для использования?. Вы должны проверить свои запросы к базе данных. И я покажу вам изящный способ легко профилировать все запросы к базе данных.
Конечно, ваш сайт может быть медленным по многим причинам, но одной из наиболее распространенных причин являются медленные запросы к базе данных.
Но в 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 [▶]
]
....
содержащий все вызовы функций до этого момента в запросе. Я сосредоточусь только на ключах и line
в каждом массивеfile
.
Если присмотреться, то можно увидеть, что в моем примере было 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 год назад.