繁体   English   中英

Laravel 数据库查询 REST API 非常慢

[英]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:第一个查询可以通过以下方式显着增强:

  1. date列上添加索引
  2. 并在不使用函数( 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM