web-dev-qa-db-ja.com

Laravel)のパフォーマンスが悪い

関係にwhere条件を適用したいと思います。これが私がすることです:

Replay::whereHas('players', function ($query) {
    $query->where('battletag_name', 'test');
})->limit(100);

次のクエリが生成されます。

select * from `replays` 
where exists (
    select * from `players` 
    where `replays`.`id` = `players`.`replay_id` 
      and `battletag_name` = 'test') 
order by `id` asc 
limit 100;

これは70秒で実行されます。このように手動でクエリを書き直すと、次のようになります。

select * from `replays` 
where id in (
    select replay_id from `players` 
    where `battletag_name` = 'test') 
order by `id` asc 
limit 100;

0.4秒で実行されます。非常に遅い場合、なぜwhere existsがデフォルトの動作であるのですか?クエリビルダーを使用して正しいwhere inクエリを生成する方法はありますか、それとも生のSQLを挿入する必要がありますか?多分私は完全に何か間違ったことをしているのですか?

replaysテーブルには400万行、playersには4000万行、関連するすべての列にインデックスが付けられ、データセットがMySQLサーバーのメモリに収まりません。

更新:正しいクエリを次のように生成できることがわかりました:

Replay::whereIn('id', function ($query) {
    $query->select('replay_id')->from('players')->where('battletag_name', 'test');
})->limit(100);

existsのパフォーマンスが非常に悪い理由と、それがデフォルトの動作である理由については、まだ疑問があります。

17
Poma

これは、laravelではなくmysqlに関連しています。 joinssubqueriesの両方のオプションを使用して、上記と同じことを実行できます。 サブクエリは、通常、結合よりもはるかに低速です。

サブクエリは次のとおりです。

  • それほど複雑ではない
  • エレガント
  • 理解しやすい
  • 書きやすい
  • 論理分離

上記の事実が、雄弁なORMがsuquriesを使用している理由です。 しかし、速度は遅くなります!特にデータベースに多くの行がある場合。

クエリの参加バージョンは次のようなものです:

select * from `replays`
join `players` on `replays`.`id` = `players`.`replay_id` 
and `battletag_name` = 'test'
order by `id` asc 
limit 100;

しかし、今度はselectを変更してgroup byを追加し、他の多くのことに注意する必要がありますが、なぜこれがその答えを超えているのですか?新しいクエリは次のようになります:

select replays.* from `replays`
join `players` on `replays`.`id` = `players`.`replay_id` 
and `battletag_name` = 'test'
order by `id` asc 
group by replays.id
limit 100;

それが、より複雑に参加する理由です。

Laravelで生のクエリを書くことはできますが、結合クエリの雄弁なサポートは十分にサポートされていません。また、それを支援するパッケージはあまりありません。これは、たとえば次のとおりです。 https://github.com/ fico7489/laravel-eloquent-join

2
fico7489

左結合を使用できます

$replies = Replay::orderBy('replays.id')
            ->leftJoin('players', function ($join) {
                $join->on('replays.id', '=', 'players.replay_id');
            })
            ->take(100)
            ->get();
0
Darany Khiev

パフォーマンスはどこに依存しないと思います選択したレコードの数にのみ依存します

さらに、mysqlサーバーを最適化してみてください

https://dev.mysql.com/doc/refman/5.7/en/optimize-overview.html

また、PHPサーバーを最適化します

クエリが高速な場合は、幼虫の生のクエリオブジェクトを使用してみませんか

$replay = DB::select('select * from replays where id in (
select replay_id from players where battletag_name = ?) 
order by id asc limit 100', ['test']
); 
0
Xain Pro