[英]Laravel DB query for REST API very slow
我正在使用 Laravel 迈出第一步,并尝试编写音乐播放列表。 该数据库有 3 个实体/模型:
Play <-n-1-> Song <-n-1-> Artist
表创建:
Schema::create('plays', function (Blueprint $table) {
$table->id();
$table->bigInteger('song_id')->unsigned();
$table->foreign('song_id')
->references('id')
->on('songs')
->onDelete('cascade');
$table->dateTimeTz('date');
$table->bigInteger('station_id')->unsigned();
$table->foreign('station_id')
->references('id')
->on('stations');
$table->timestamps();
$table->unique(['song_id', 'date']);
});
Schema::create('songs', function (Blueprint $table) {
$table->id();
$table->bigInteger('artist_id')->unsigned();
$table->foreign('artist_id')
->references('id')
->on('artists')
->onDelete('cascade');
$table->string('title');
$table->string('cover_url')->nullable();
$table->smallInteger('cover_width')->nullable();
$table->smallInteger('cover_height')->nullable();
$table->string('asin')->nullable();
$table->date('last_cover_check')->nullable();
$table->timestamps();
$table->unique(['artist_id', 'title']);
});
Schema::create('artists', function (Blueprint $table) {
$table->id();
$table->string('name')->unique();
$table->timestamps();
});
该数据库有大约 856000 次播放,33000 首歌曲和 15000 名艺术家。
起初,我尝试直接使用模型直接加载plays->songs->artist。
return PlayResource::collection(Play::whereDate('date', $date)->get());
使用 Insomnia,通过 Laravel 对 REST API 进行此查询需要 2.6 秒。 这很慢,我想。
如果我正确理解 Laravel 它会做很多数据库查询:
因此,我尝试创建一个 JOIN 查询,使其只有一个 DB 查询:
return DB::table('plays')
->leftJoin('songs', 'plays.song_id', '=', 'songs.id')
->leftJoin('artists', 'songs.artist_id', '=', 'artists.id')
->select('plays.*', 'songs.*', 'artists.*')
->whereDate('plays.date', $date)
->orderByDesc('plays.date')
->get();
这个查询有点快但仍然很慢:2.2s
直接在 DB 上调用的等效 SQL 查询要快得多:
SELECT *
FROM plays p
LEFT JOIN songs s ON p.song_id = s.id
LEFT JOIN artists a ON s.artist_id = a.id
WHERE DATE(p.date) = "2020-12-30"
ORDER BY p.date DESC;
-> 0.4s
使用 Laravel 时我做错了什么还是典型的开销?
编辑1:
好的,我找到了DB::getQueryLog()
(但这不是我要找的)。 现在我知道了:使用三个带有链式预加载的表只会导致 3 个数据库查询。 我的第一个是最慢的,将近 400 毫秒。 这两个查询每个只需要 1-2 毫秒。
我还发现流明应该比 Laravel 快。 所以我试了一下。 我使用模型的第一个查询现在需要 1.4 秒。 为 output 映射添加 JsonResources 会增加 0.3s -> 1.7s。 在我看来,所有这一切仍然是缓慢的。
但我认为这是我使用现代框架必须付出的代价。 (我 2006 年的旧播放列表不使用任何框架)。
EDIT2:第一个查询可以通过以下方式显着增强:
date
列上添加索引DATE(`date`)
或->whereDate('date', ...)
)的情况下进行查询,而是使用->whereBetween('date', [$date, $date.' 23:59:59'])
现在第一个查询只需要 5 毫秒(从之前的 400 毫秒)。 整个流明查询我是 1.3 秒。SQL 查询现在只需 <10 毫秒。 所以流明开销约为 99%。 特别糟糕。 我将寻找进一步的调整潜力。
EDIT3我现在确实使用 Rust(actix_web,diesel)创建了这个完全相同的 REST API,并且使用相同数据库的相同请求需要 16 毫秒。 这些 PHP 框架有多慢真是太疯狂了。 我现在将专注于 Rust 并为后端放弃 PHP/Laravel/Lumen。
您可以通过将以下 function 添加到您的应用程序/提供程序/AppServiceProvider.php 文件来检查 Laravel 向数据库启动的查询:
public function boot(){
if(env('APP_DEBUG')) {
DB::listen(function($query) {
File::append(
storage_path('/logs/query.log'),
$query->sql . ' [' . implode(', ', $query->bindings) . ']' . PHP_EOL
);
});
}
}
您将在将创建的 /storage/logs/query.log 文件中找到每个查询的 sql 代码。 这有助于我理解和优化查询构建器语句,有时我也遇到过类似的问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.