如何优化 Laravel Eloquent 查询设计和关系?
How to optimize Laravel Eloquent query design and relationships?
我一直在努力学习更多关于如何更好地优化我的 eloquent 查询的方法。我知道它涉及拥有更强大的数据库设计并以 "right" 方式从服务器请求数据。
应用程序
我有一个示例应用程序,可让您添加任务列表、删除任务列表以及编辑它们(主要是标题)。对于每个任务列表,您可以添加任务、删除任务和编辑任务。典型的 CRUD 应用程序。
代码
我不会 post 我的 Vue 组件,除非有要求,因为我不确定它是否相关。我对初始查询优化更感兴趣。我的关系设置如下:
型号:User.php
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\Models\TaskList;
use App\Task;
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'first', 'last', 'email', 'phone', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function lists() {
return $this->hasMany(TaskList::class,'user_id');
}
}
型号:TaskList.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use User;
use App\Models\Task;
class TaskList extends Model
{
protected $fillable = ['user_id','status','title'];
public function user() {
return $this->belongsTo(User::class);
}
public function tasks() {
return $this->hasMany(Task::class,'list_id');
}
public function scopeLists() {
return $this->where('user_id',auth()->user()->id);
}
}
型号:Task.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Models\TaskList;
use App\User;
class Task extends Model
{
public function list() {
return $this->belongsTo(TaskList::class);
}
}
控制器:TaskListController.php @index
public function index()
{
$user = auth()->user()->load('lists.tasks');
$data = [
'lists' => $user->lists,
];
return view('user.tasklist',$data);
}
在我看来,我并没有做任何特别的事情。只需将我的数据粘贴到我的 Vues 数组成员 lists
:
let app = new Vue({
el: '#app',
data: {
lists: []
}
});
@if(isset($lists))
@foreach($lists as $list)
app.lists.push({
id: '{{ $list->id }}',
title: '{!! addslashes($list->title) !!}',
status: '{{ $list->status }}',
tasks: JSON.parse('{!! $list->tasks !!}')
});
@endforeach
@endif
问题
问题很简单,我的加载时间大约是 1 秒。我在网上阅读了混合信息,认为 Laravel 是一个缓慢的框架,其他人声称他们可以获得低于 20 毫秒的加载时间等等。当然,我知道我在开发环境中,我将看到与生产环境不同的结果。我也知道我有开发包,不一定会在生产中加载,这会改善引导。但我认为下面的这些数字非常慢,而且这里还有其他事情。
问题
简单地说,在这种情况下我该如何提高性能并优化我的应用程序?
我想知道在这种特定情况下我可以采取哪些步骤来提高我的应用程序性能。在我的开发箱中,完全加载了我所有的开发包,我可以实现低于 500 毫秒的加载时间。甚至 500 毫秒似乎也很慢。能否实现低于 200 毫秒或低于 100 毫秒?这只是硬件问题吗?还是我对我的人际关系和加载完全错误?
"require": {
"php": ">=7.1.3",
"fideloper/proxy": "~4.0",
"laravel/framework": "5.6.*",
"laravel/tinker": "~1.0"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.1",
"filp/whoops": "~2.0",
"fzaninotto/faker": "~1.4",
"mockery/mockery": "~1.0",
"phpunit/phpunit": "~7.0",
"symfony/thanks": "^1.0"
},
非常感谢任何对此进行优化的见解!
关于您的代码
据我所知,您正在以正确的方式查询数据库,您正在使用 lazy eager loading,它执行的查询与预先加载的查询完全相同。
您的数据库可能有问题。您没有发布迁移。 task_lists.user_id
和 tasks.list_id
上的外键必须分别引用 user.id
和 task_lists.id
。
其他可能的原因
- 您说您的整个请求需要 1 秒,并且专注于查询,但您没有在 Laravel 调试栏上显示实际查询时间。
- 注意:启动时间为~600ms,执行时间为~300ms。您的查询在执行时间内(还有很多其他事情)。
- 启动时间主要受文件系统访问缓慢的影响,这是由常见的坏蛋 HDD 引起的。更换为 SSD,您会发现有很大的改进。
- Laravel 在带有 Laravel Debugbar 运行 的调试模式下比在生产模式下慢得多。这是因为启用了数据库查询记录器以及其他原因。
- 如果您安装了 php xdebug,它也会 slow down your execution time。
- 从 Laravel 生产中删除您不使用的所有内容。不使用 cookie?为什么加载
CookieServiceProvider
?不使用文件系统?为什么加载 FilesystemServiceProvider
?查看您的 app.providers
配置。这将减少启动时间,因为您将加载更少的文件。
好了,这些都解决了,之后如何提高性能?
假设您的环境已优化,您的代码已优化,但您的响应时间仍然很慢,是时候 Profile your code. With the right profiler tool 您将确切地看到哪些代码正在变慢。
如果数据库查询本身很慢并且您无能为力,您可以使用 Laravel's Cache 和 memcached 或 redis 驱动程序,并缓存您的慢速查询,而不是查询数据库对于结果,它们将在内存中并在几微秒内可用。
我一直在努力学习更多关于如何更好地优化我的 eloquent 查询的方法。我知道它涉及拥有更强大的数据库设计并以 "right" 方式从服务器请求数据。
应用程序
我有一个示例应用程序,可让您添加任务列表、删除任务列表以及编辑它们(主要是标题)。对于每个任务列表,您可以添加任务、删除任务和编辑任务。典型的 CRUD 应用程序。
代码
我不会 post 我的 Vue 组件,除非有要求,因为我不确定它是否相关。我对初始查询优化更感兴趣。我的关系设置如下:
型号:User.php
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\Models\TaskList;
use App\Task;
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'first', 'last', 'email', 'phone', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function lists() {
return $this->hasMany(TaskList::class,'user_id');
}
}
型号:TaskList.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use User;
use App\Models\Task;
class TaskList extends Model
{
protected $fillable = ['user_id','status','title'];
public function user() {
return $this->belongsTo(User::class);
}
public function tasks() {
return $this->hasMany(Task::class,'list_id');
}
public function scopeLists() {
return $this->where('user_id',auth()->user()->id);
}
}
型号:Task.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Models\TaskList;
use App\User;
class Task extends Model
{
public function list() {
return $this->belongsTo(TaskList::class);
}
}
控制器:TaskListController.php @index
public function index()
{
$user = auth()->user()->load('lists.tasks');
$data = [
'lists' => $user->lists,
];
return view('user.tasklist',$data);
}
在我看来,我并没有做任何特别的事情。只需将我的数据粘贴到我的 Vues 数组成员 lists
:
let app = new Vue({
el: '#app',
data: {
lists: []
}
});
@if(isset($lists))
@foreach($lists as $list)
app.lists.push({
id: '{{ $list->id }}',
title: '{!! addslashes($list->title) !!}',
status: '{{ $list->status }}',
tasks: JSON.parse('{!! $list->tasks !!}')
});
@endforeach
@endif
问题
问题很简单,我的加载时间大约是 1 秒。我在网上阅读了混合信息,认为 Laravel 是一个缓慢的框架,其他人声称他们可以获得低于 20 毫秒的加载时间等等。当然,我知道我在开发环境中,我将看到与生产环境不同的结果。我也知道我有开发包,不一定会在生产中加载,这会改善引导。但我认为下面的这些数字非常慢,而且这里还有其他事情。
问题
简单地说,在这种情况下我该如何提高性能并优化我的应用程序?
我想知道在这种特定情况下我可以采取哪些步骤来提高我的应用程序性能。在我的开发箱中,完全加载了我所有的开发包,我可以实现低于 500 毫秒的加载时间。甚至 500 毫秒似乎也很慢。能否实现低于 200 毫秒或低于 100 毫秒?这只是硬件问题吗?还是我对我的人际关系和加载完全错误?
"require": {
"php": ">=7.1.3",
"fideloper/proxy": "~4.0",
"laravel/framework": "5.6.*",
"laravel/tinker": "~1.0"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.1",
"filp/whoops": "~2.0",
"fzaninotto/faker": "~1.4",
"mockery/mockery": "~1.0",
"phpunit/phpunit": "~7.0",
"symfony/thanks": "^1.0"
},
非常感谢任何对此进行优化的见解!
关于您的代码
据我所知,您正在以正确的方式查询数据库,您正在使用 lazy eager loading,它执行的查询与预先加载的查询完全相同。
您的数据库可能有问题。您没有发布迁移。 task_lists.user_id
和 tasks.list_id
上的外键必须分别引用 user.id
和 task_lists.id
。
其他可能的原因
- 您说您的整个请求需要 1 秒,并且专注于查询,但您没有在 Laravel 调试栏上显示实际查询时间。
- 注意:启动时间为~600ms,执行时间为~300ms。您的查询在执行时间内(还有很多其他事情)。
- 启动时间主要受文件系统访问缓慢的影响,这是由常见的坏蛋 HDD 引起的。更换为 SSD,您会发现有很大的改进。
- Laravel 在带有 Laravel Debugbar 运行 的调试模式下比在生产模式下慢得多。这是因为启用了数据库查询记录器以及其他原因。
- 如果您安装了 php xdebug,它也会 slow down your execution time。
- 从 Laravel 生产中删除您不使用的所有内容。不使用 cookie?为什么加载
CookieServiceProvider
?不使用文件系统?为什么加载FilesystemServiceProvider
?查看您的app.providers
配置。这将减少启动时间,因为您将加载更少的文件。
好了,这些都解决了,之后如何提高性能?
假设您的环境已优化,您的代码已优化,但您的响应时间仍然很慢,是时候 Profile your code. With the right profiler tool 您将确切地看到哪些代码正在变慢。
如果数据库查询本身很慢并且您无能为力,您可以使用 Laravel's Cache 和 memcached 或 redis 驱动程序,并缓存您的慢速查询,而不是查询数据库对于结果,它们将在内存中并在几微秒内可用。