>  기사  >  PHP 프레임워크  >  Laravel에서 가장 느린 쿼리를 찾는 방법에 대한 간략한 분석

Laravel에서 가장 느린 쿼리를 찾는 방법에 대한 간략한 분석

青灯夜游
青灯夜游앞으로
2022-09-08 10:09:002402검색

웹사이트 속도가 느린가요? 로딩하는데 시간이 오래 걸리나요? 사용자들이 거의 사용할 수 없다고 불평하고 있나요? 데이터베이스 쿼리를 확인해야 합니다. 모든 데이터베이스 쿼리를 쉽게 분석할 수 있는 깔끔한 방법을 보여 드리겠습니다.

Laravel에서 가장 느린 쿼리를 찾는 방법에 대한 간략한 분석

물론 웹 사이트가 느린 데에는 여러 가지 이유가 있지만 가장 일반적인 이유 중 하나는 느린 데이터베이스 쿼리입니다.

그러나 laravel에서는 (대부분의 경우) 데이터베이스에서 데이터를 가져오기 위해 SQL을 사용하지 않고 Eloquent ORMQuery Builder를 사용합니다. 이로 인해 때때로 사이트 오류를 ​​일으키는 쿼리를 정확히 찾아내기가 어렵습니다. 너무 천천히 하세요.

DB::listen()

다행히도 laravel에서는 쿼리가 실행될 때마다 호출되는 콜백을 정의할 수 있습니다(여기 참조). 이렇게 하려면 서비스 제공자(예: AppServiceProvider)에 다음 코드를 추가하세요.

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

보시다시피 QueryExecuted$query,这个变量是 QueryExecuted 类的一个实例。这意味着我们可以访问有关已执行查询的一些信息:

 DB::listen(function ($query) {
     $query->sql; // 执行的 sql 字符串
     $query->bindings; // 传递给sql查询的参数(这将替换sql字符串中的 "?")
     $query->time; // 执行查询所用的时间;
 });

这是非常有用的信息,现在我们可以通过查看 $query->time 属性来识别慢查询。 但这并没有告诉我们在我们的代码中查询执行的位置

我怎么知道查询是在哪里执行的?

即使该 $query 变量没有给我们任何关于其来源的信息, 我们仍然可以使用 PHP 内置函数  debug_backtrace() 获取该信息。

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

这是一个数组,其中包含请求中到目前为止的每个函数调用。我将只关注每个数组中的 fileline 键。

如果你仔细看,你会发现在我的例子中有 63 个函数调用,这是一个简单的应用程序,如果在更复杂的应用程序中,可能会更多。更糟糕的是,如果您查看顶部的那些, 它们都是 laravel 框架的内部函数。我们是否应该逐一查看,直到找到可能对我们有帮助的东西?

查找查询位置

正如我之前所说,它们中的大多数是内部框架调用,这意味着这些文件中的大多数都在我们的 vendor/ 目录中。这意味着我们可以检查每个 file 并过滤掉任何具有 vendor/ 的调用,如下所示:

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

    dd($stackTrace);
});

在这里,我将数组转换为集合以使用该 filter 方法,如果 file 当前 $tracevendor/ 클래스의 인스턴스입니다. 이는 실행된 쿼리에 대한 일부 정보에 액세스할 수 있음을 의미합니다.

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
}

이는 매우 유용한 정보입니다. 이제 $query->time 속성을 ​​보고 느린 쿼리를 식별할 수 있습니다.

그러나 이는 코드에서 쿼리가 실행되는 위치를 알려주지 않습니다

.

쿼리가 실행된 위치를 어떻게 알 수 있나요?

$query 변수가 원본에 대한 정보를 제공하지 않더라도 PHP 내장 함수 debug_backtrace()를 사용하여 해당 정보를 얻을 수 있습니다. .

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

이것은 요청에서 지금까지 모든 함수 호출을 포함하는 배열입니다. 각 배열의 fileline 키에만 집중하겠습니다.

자세히 살펴보면 제 예에는 63개의 함수 호출이 있다는 것을 알 수 있습니다. 이는 간단한 애플리케이션이지만 더 복잡한 애플리케이션에서는 더 많을 수도 있습니다. 더 나쁜 것은, 맨 위에 있는 것을 보면 모두 Laravel 프레임워크의 내부 기능입니다. 우리에게 도움이 될 만한 것을 찾을 때까지 각각을 살펴보아야 합니까?

쿼리 위치 찾기

앞서 말했듯이 대부분은 내부 프레임워크 호출이므로 이러한 파일의 대부분은 vendor/ 디렉토리 중간에 있습니다. 즉, 각 파일을 확인하고 다음과 같이 vendor/를 사용한 호출을 필터링할 수 있습니다.

rrreee

여기서 배열을 컬렉션으로 변환합니다. 필터 사용 메서드, 현재 $tracevendor/가 있는 파일이 있으면 컬렉션에서 해당 파일을 제거합니다. 위의 코드를 실행하면 다음과 같은 내용이 표시됩니다.

rrreee

항목이 훨씬 적습니다. 63개에서 5개로 줄었습니다. 가장 좋은 점은 컬렉션의 첫 번째 항목이 SQL 쿼리를 트리거하는 정확한 위치라는 것입니다. 이는 이 정보를 추출하여 가장 느린 쿼리를 찾을 수 있음을 의미합니다.

로그로 인쇄

이제 필요한 모든 정보를 얻었으니 가장 느린 쿼리를 검사하고 찾을 수 있도록 기록해 보는 것은 어떨까요? :

rrreee🎜애플리케이션에서 이것을 사용하는 경우 로그 파일을 확인할 수 있으며 다음과 같은 쿼리 정보가 표시됩니다.🎜rrreee🎜이제 어떤 쿼리가 가장 느린지 알고 하나씩 처리하기 시작합니다. 더 빠르거나 적어도 캐시합니다. 🎜🎜🎜🎜🎜확장 디버깅🎜🎜🎜이것은 디버깅에 적합하지만 이 기술은 다양한 방법으로 사용될 수 있습니다. 🎜🎜🎜 이번 주 가장 느린 쿼리를 보여주는 주간 보고서를 만들 수 있습니다. 🎜🎜🎜🎜쿼리가 시간 임계값을 초과하면 Slack 알림을 받을 수 있습니다. 🎜🎜🎜🎜 귀하와 팀이 실행된 모든 쿼리를 볼 수 있는 대시보드를 만들 수 있습니다. 🎜🎜🎜무한합니다. 🎜🎜【관련 추천: 🎜laravel 동영상 튜토리얼🎜】🎜

위 내용은 Laravel에서 가장 느린 쿼리를 찾는 방법에 대한 간략한 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 learnku.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제