Laravel/Lumen 記錄MySQL 和 MongoDB 產生的 SQL,定位 SQL 產生位置

ruby發表於2020-07-26

不知道大家是否和我一樣,在開發的過程中經常想看看產生的 SQL 語句有多少,又或者檢視到某一個 SQL 語句很慢的時候,需要定位到對應的程式碼位置去進行修改。有一些現成的工具幫我們做了這些事,比如 barryvdh/laravel-debugbar,但可能有時候我們只是想看一看 SQL,其他的東西不是太關心。

鑑於以上原因,自己寫了一個包,來幫助自己定位程式碼產生的 SQL,以便做一些最佳化什麼的。有興趣的可以用一下。程式碼在 sql-logger

一個記錄 SQL 語句的工具,也可以記錄 jenssegers/mongodb 包產生的語句。

用途

  1. 記錄某一個 SQL 語句產生於哪一行程式碼。

  2. 記錄 MySQL 或者 MongoDB 產生的語句及其耗時。

安裝

1.透過 composer 安裝 (eleven26/sql-logger)。

composer require "eleven26/sql-logger:~1.0.0" --dev

2.註冊 Service Provider

  • Laravel: 修改檔案 config/app.phpLaravel 5.5+ 不需要

      'providers' => [
          //...
          Eleven26\SqlLogger\SqlLoggerServiceProvider::class,
      ],
  • Lumen: 修改檔案 bootstrap/app.php,新增下面這一行

      $app->register(Eleven26\SqlLogger\SqlLoggerServiceProvider::class);
  1. config 資料夾下面新增一個配置檔案,名為 sql-logger.php,複製如下內容進去:
<?php

return [
    // 是否啟用 sql-logger
    'enable_sql_logger' => env('ENABLE_SQL_LOGGER', true),

    // config/database.php connections 裡面配置的連線名
    'mysql_connections' => [

    ],

    // config/database.php connections 裡面配置的連線名
    'mongodb_connections' => [

    ],
];

用法

下面提到的資料庫連線名來自於 config/database.php 裡面 connections 陣列的鍵。

監聽

  1. 首先在配置檔案 config/sql-logger.php 裡面新增連線名稱,MySQL 連線名加到 mysql_connections 陣列中,如:
'mysql_connections' => [
    "user", 
],
  1. 如果需要啟用 MongoDB 的監聽,在 mongodb_connections 陣列中新增對應的連線名即可。

如何啟用監聽

  1. 使用 enable_mysql_log 來監聽 MySQL 產生的 SQL 語句,使用 enable_mongo_log 來監聽 MongoDB 產生的語句。

  2. 如果只是想對某幾行程式碼進行除錯,可以在那幾行程式碼之後使用 disable_mysql_log 或者 disable_mongo_log 來禁用後續 SQL 語句的記錄。

如何定位 SQL 語句產生的地方

有時候我們看到程式碼產生了一些慢查詢、重複查詢等,這個時候我們想去定位是哪一行程式碼產生的話。可以給 enable_mysql_logenable_mongo_log 傳入一個引數,引數內容是 SQL 語句的一部分,最好是可以完全和輸出的其他 SQL 區分開的。

寫 Log 的時候,會判斷當前要記錄的 SQL 語句是否包含了傳遞的引數字串,如果包含,則記錄產生該 SQL 的堆疊。這樣我們就知道是哪一行程式碼導致了這個 SQL 語句的出現。

例項

  1. 模型
class User extends Model
{
    protected $connection = 'user';
}
  1. config/database.php
return [
    // ...
    "connections" => [
        "user" => [
            // ...具體配置項
        ]
    ],
    // ...
];
  1. 記錄 SQL 語句及其時間:
enable_mysql_log();

app(User::class)->first();
  1. 檢視日誌檔案,可以看到如下內容:
[2020-07-26 20:13:59] lumen.INFO: time: 37.42ms select * from `users` where `users`.`deleted_at` is null limit 1
  1. 檢視這個 SQL 語句是哪一行產生的,只需要給 enable_mysql_log 傳遞 SQL 語句中可以區別開其他 SQL 語句的子字串進去即可,如:
enable_mysql_log('select * from `users`');

app(User::class)->first();

再次檢視日誌檔案可以看到如下內容:

[2020-07-26 20:16:42] lumen.INFO: time: 30.40ms select * from `users` where `users`.`deleted_at` is null limit 1  
[2020-07-26 20:16:42] lumen.INFO: 
#0 /Users/ruby/code/Foundation/vendor/illuminate/events/Dispatcher.php(350): Eleven26\SqlLogger\MysqlLogService::Eleven26\SqlLogger\{closure}(Object(Illuminate\Database\Events\QueryExecuted))
#1 /Users/ruby/code/Foundation/vendor/illuminate/events/Dispatcher.php(200): Illuminate\Events\Dispatcher->Illuminate\Events\{closure}('Illuminate\\Data...', Array)
#2 /Users/ruby/code/Foundation/vendor/illuminate/database/Connection.php(825): Illuminate\Events\Dispatcher->dispatch('Illuminate\\Data...')
#3 /Users/ruby/code/Foundation/vendor/illuminate/database/Connection.php(682): Illuminate\Database\Connection->event(Object(Illuminate\Database\Events\QueryExecuted))
#4 /Users/ruby/code/Foundation/vendor/illuminate/database/Connection.php(635): Illuminate\Database\Connection->logQuery('select * from `...', Array, 214.4)
#5 /Users/ruby/code/Foundation/vendor/illuminate/database/Connection.php(333): Illuminate\Database\Connection->run('select * from `...', Array, Object(Closure))
#6 /Users/ruby/code/Foundation/vendor/illuminate/database/Query/Builder.php(1719): Illuminate\Database\Connection->select('select * from `...', Array, true)
#7 /Users/ruby/code/Foundation/vendor/illuminate/database/Query/Builder.php(1704): Illuminate\Database\Query\Builder->runSelect()
#8 /Users/ruby/code/Foundation/vendor/illuminate/database/Eloquent/Builder.php(481): Illuminate\Database\Query\Builder->get(Array)
#9 /Users/ruby/code/Foundation/vendor/illuminate/database/Eloquent/Builder.php(465): Illuminate\Database\Eloquent\Builder->getModels(Array)
#10 /Users/ruby/code/Foundation/vendor/illuminate/database/Concerns/BuildsQueries.php(77): Illuminate\Database\Eloquent\Builder->get(Array)
#11 /Users/ruby/code/Foundation/vendor/illuminate/database/Eloquent/Model.php(1477): Illuminate\Database\Eloquent\Builder->first()
#12 /Users/ruby/code/Foundation/test.php(9): Illuminate\Database\Eloquent\Model->__call('first', Array)
#13 {main} 

這裡的第二條 log 記錄了 SQL 語句產生的堆疊,開發者可以依據這些資料來精確判定是哪一行產生的 SQL。

最佳實踐

  1. 執行 composer require 的時候,加上 --dev

  2. 生產配置檔案 .env 中,加上配置:ENABLE_SQL_LOGGER=false

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章