【问题标题】:Laravel Slow queriesLaravel 慢查询
【发布时间】:2020-10-30 20:08:49
【问题描述】:
public function delete( ReportDetailRequest $request )
    {
        $id = (int)$request->id;
        $customerRecord = CustomerInfo::find($id);
        $customerRecord->delete();
    }

我目前在 laravel 应用程序中具有上述内容,其中 DELETE 请求被发送到此控制器。目前,如您所见,它非常简单,但查询似乎超级慢。它在 2.23 秒内返回邮递员。我应该怎么做才能加快速度?据我所知,数据库层(mysql)确实有一个关于 ID 的索引,并且应用程序没有在调试中运行。这是典型的吗?

编辑: 很好地认为请求验证可能正在做某事(它正在验证该用户具有要删除的身份验证)。

class ReportDetailRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        $id = (int)$this->route('id');
        $customerInfo = CustomerInfo::find($id)->first();
        $company = $customerInfo->company_id;
        return (auth()->user()->company->id  == $company );
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            //
        ];
    }
}

显示创建表:

CREATE TABLE "customer_info" (
  "id" int(11) NOT NULL AUTO_INCREMENT,
  "user_id" int(11) DEFAULT NULL,
  "report_guid" varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  "customer_email" varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  "created_at" timestamp NULL DEFAULT NULL,
  "updated_at" timestamp NULL DEFAULT NULL,
  "report_read" tinyint(1) NOT NULL,
  "customer_name" varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  "customer_support_issue" longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
  "company_id" int(11) NOT NULL,
  "archived" tinyint(1) NOT NULL,
  "archived_at" timestamp NULL DEFAULT NULL,
  "report_active" tinyint(4) DEFAULT NULL,
  "customer_screenshot" varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  "video_url" varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY ("id"),
  KEY "indexReportLookup" ("report_guid"),
  KEY "guid" ("report_guid"),
  KEY "customer_info_id_index" ("id")
)

基线:

 public function delete( Request $request )
    {
        // $id = (int)$request->id;
        // $customerRecord = CustomerInfo::find($id);
        // $foo_sql = $customerRecord->delete()->toSql();
        // echo($foo_sql);
        return 'test';
        //$customerRecord->delete();
    }

好的,这是一张全新的桌子,带有全新的请求。里面只有一个 ID,看起来像这样:

控制器看起来像:

public function deleteTest( Request $request )
    {
        $id = (int)$request->id;
        $customerRecord = NewTable::where('id', '=', $id)->first();
        $customerRecord->delete();
        return response(null, 200);
    }

Postman 版本为:Version 7.27.1 (7.27.1)

1630 毫秒。哇。对新表的简单请求需要 1.6 秒。

解释删除:

1   DELETE  new_tables      range   PRIMARY PRIMARY 8   const   1   100 Using where

解释选择

1   SIMPLE  new_tables      const   PRIMARY PRIMARY 8   const   1   100 Using index

MYSQL 版本 8.0.18 innodb_version 8.0.18


所以现在来增加乐趣。

一个无框架的 PHP 文件。简单的 GET 请求。 100 毫秒。

<?php
echo('tester');
?>

编辑。只是重申一下。

一个 Laravel GET 方法(带认证)返回测试,返回 1.6s。

一个无框架的“sample.php”文件在 100 毫秒内返回。

一个 Laravel GET 方法(无需认证)返回测试,430ms 返回。

一个 Laravel GET 方法(没有身份验证但有数据库访问),在 1483 毫秒内返回。

一旦应用程序开始使用数据库,似乎确实有一些东西阻碍了请求。

Route::middleware('auth:api')->get('/test1','Api\CustomerInfoController@deleteTest')->name('report.deleteTest1.api');
Route::middleware('auth:api')->get('/test2','Api\NewTableController@index')->name('report.deleteTest2.api');

Route::get('/test3','Api\CustomerInfoController@deleteTest')->name('report.deleteTest3.api');
Route::get('/test4','Api\NewTableController@index')->name('report.deleteTest4.api');
 Route::get('/test5','Api\NewTableController@dbTest')->name('report.deleteTest5.api');

新表控制器:

<?php

namespace App\Http\Controllers\Api;

class NewTableController extends Controller
{

    
    public function index()
    {
        return "test2";
    }



}

CustomerInfoController(删除了一些东西,但方法在概念上与 NewTableController 非常相似,尽管进行了一些依赖注入)。

<?php

namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Http\Requests\ReportDetailRequest;
use App\Services\CustomerInfoService;
use Auth;
use App\LookupParent;
use App\LookupChild;
use App\CustomerInfo;
use App\Http\Resources\CustomerInfoResourceCollection;
use App\Http\Resources\CustomerInfoResource;
use App\Http\Resources\CustomerInfoResourceDetail;
use Carbon\Carbon;
use App\NewTable;

class CustomerInfoController extends Controller
{
    protected $customerInfoService;

    public function __construct(
        CustomerInfoService $customerInfoService
        )
    {
        $this->customerInfoService = $customerInfoService;
    }


    public function deleteTest()
    {
        return 'deleteTest';
    }


   public function dbTest()
   {   
     tap(NewTable::find(1))->delete();
   }


}

结果:

/test1 (with authentication 1380ms)
/test2 (with authentication 1320ms)
/test3 (without authentication 112ms)
/test4 (without authentication 124ms)
/test5 (db without authentication 1483ms)

换句话说,身份验证与数据库的对话就像没有身份验证的简单删除查询一样。这些至少需要一秒钟才能完成。这会导致上面提到的大约两秒钟的请求,其中包含两个元素(身份验证和数据库访问)。

编辑。对于那些从谷歌阅读的人。问题与 Digital Ocean 提供的托管数据库有关。在同一个机器上在 MySQL 上设置本地化数据库,问题自行解决。认为这是来自世界各地 Web 服务器和数据库之间的数据中心的延迟,或者是 DigitalOcean 的数据库管理员配置错误。自己解决了,问题不是 Laravel。

【问题讨论】:

  • 您能测量获取结果所需的时间和删除结果所需的时间吗?在方法的开头放置 $startTime = microtime(true); $customerRecord = NewTable::where('id', '=', $id)->first(); 之后将 var_dump(microtime(true) - $startTime) 和相同的东西放在方法的末尾。产生了什么价值?
  • 很好地解释了您的问题,您已经竭尽全力深入了解它。我没有看到的一项是数据库和 php 服务器之间的连接?它们是在同一个服务器上,还是在同一个网络中,还是距离更远?这可能会影响您的应用程序的速度。
  • 你能给我们一些关于它运行的服务器环境的信息吗?这看起来不像是生产服务器,如果至少部分瓶颈在服务器配置中,我不会感到惊讶
  • 我没有看到您指定 Laravel 和 PHP 版本,但您是否尝试过为此使用工具进行基准测试?您可能在其他地方处理缓慢,而不仅仅是 SQL。
  • 如果你愿意的话,试着对 Lumen 做同样的事情。它是 Laravel 的精简版,特别是为了在更短的时间内快速处理更多请求而设计。或者尝试放弃 Eloquent 并尝试编写原始查询,看看是否有任何不同。还可以尝试更改您的 DB 和 DB 驱动程序以使用其他东西进行测试(也许是 PostgreSQL?)。您的数据库是否托管在同一台机器(本地主机)上?

标签: mysql laravel performance


【解决方案1】:

你可以试试这个:

use CustomerInfo;

public function delete( CustomerInfo $customer)
    {
        $customer->delete();
    }

在你的 routes.php 中

Route::delete('/customer-info/{customer}','CustomerInfoController@delete');

【讨论】:

    【解决方案2】:

    响应慢的主要原因是DB被调用了两次,一次是查找记录,然后是删除。相反,您应该这样做。这在删除大编号时也很棒。记录。

    $ids = explode(",", $id);
    CustomerInfo::whereIn('id', $ids)->delete();
    

    【讨论】:

      【解决方案3】:

      你不需要从数据库中获取记录来删除它

      public function delete( ReportDetailRequest $request )
      {
         
          return CustomerInfo::where('id',$request->input('id'))->delete();
          // it will return the count of deleted rows
      }
      

      【讨论】:

        【解决方案4】:

        尝试删除记录而不加载它:

        public function delete( ReportDetailRequest $request )
        {
           
            $customerRecord = CustomerInfo::where('id',$request->id)->delete();
            
        }
        

        请注意,您不必将 $request->id 转换为 int

        【讨论】:

        • 不强制转换不会让你更容易受到 sql 注入的攻击吗?
        • Laravel 查询构建器使用 PDO 参数绑定来保护您的应用程序免受 SQL 注入攻击。无需清理作为绑定传递的字符串。 laravel.com/docs/8.x/queries#introduction
        • 任何方式,你都可以施放,更安全,而且会更快
        【解决方案5】:

        Mysql 8 在启动时引入二进制日志。要在 Mysql 8 中禁用二进制日志记录,您需要使用 --disable-log-bin 启动 MySQL 服务器。据我所知,禁用上面的这个,你的速度会提高至少 10%。

        如果您需要更多解释,请访问此线程https://dba.stackexchange.com/a/216624

        【讨论】:

          猜你喜欢
          • 2020-01-16
          • 2020-03-04
          • 2021-06-23
          • 1970-01-01
          • 2018-02-27
          • 2021-04-11
          • 2021-04-01
          • 1970-01-01
          • 2018-01-30
          相关资源
          最近更新 更多