1つの大きなクエリまたは複数の小さなクエリの高速化とは何ですか?


68

私はさまざまな企業で働いていますが、一部の企業は、すべての「相対」テーブルを結合するビューを持つことを好むことに気付きました。しかし、その後、アプリケーション上で使用する必要があるのは1列のみです。

それでは、単純な選択を行い、それをシステムコードで「結合」する方が速いでしょうか?

システムは、php、java、asp、データベースに接続する任意の言語です。

だから問題は、サーバー側(php、java、asp、ruby、python ...)からデータベースに行く方が速いか、必要なものをすべて取得する1つのクエリを実行するか、サーバー側からデータベースに移動して一度に1つのテーブルから列のみを取得するクエリ?


2
「SQL」のどの実装を使用していますか?MySQL、Microsoft SQL Server、Oracle、Postgresqlなど?タグを更新してください。
RLF 14

1
MySQLとPostgresql
sudo.ie 14

6
私の経験では、MySQLは複雑なクエリを好まず、非常に単純なクエリ(通常はそれ以上)で高速になります。Postgresのクエリオプティマイザーははるかに優れており、通常、1つの大きなクエリを実行する方が効率的です。
a_horse_with_no_name 14

3
@a_horse_with_no_nameこれは、特にこの質問の文脈において、非常に広範な一般化です。MySQLオプティマイザーは確かに設計上非常にシンプルであり、結合とサブクエリ(特に古いバージョンのMySQL)で問題を引き起こす可能性があります。ただし、質問のコンテキストでは、1つの大きなクエリの方が高速です。たとえば、最悪のシナリオでは、プログラミングループ内のSELECT(使用されるRDBMSに関係なく)です。
jynus

2
@jynus:まあ、質問非常に広範です(プラス:「私の経験」と言った-他の人は異なる経験を持っているかもしれません)。LOOP内のクエリは決して良いアイデアではなく、ほとんどの場合、設計が不十分であるか、リレーショナルデータベースを操作する方法を理解していないことが原因です。
a_horse_with_no_name 14

回答:


69

あなたの質問に対処するのは、「JOIN DECOMPOSITION」という主題です。

本の209ページによると

高性能MySQL

マルチテーブル結合の代わりに複数のシングルテーブルクエリを実行し、アプリケーションで結合を実行することにより、結合を分解できます。たとえば、この単一のクエリの代わりに:

SELECT * FROM tag
JOIN tag_post ON tag_post.tag_id = tag.id
JOIN post ON tag_post.post_id = post.id
WHERE tag.tag = 'mysql';

次のクエリを実行できます。

SELECT * FROM tag WHERE tag = 'mysql';
SELECT * FROM tag_post WHERE tag_id=1234;
SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904);

一体どうしてこんなことをするの?何も見返りなくクエリの数を増やしたため、一見無駄に見えます。ただし、このような再構築により、実際にパフォーマンスが大幅に向上します。

  • キャッシングはより効率的です。多くのアプリケーションは、テーブルに直接マップする「オブジェクト」をキャッシュします。この例では、タグを持つオブジェクトmysqlが既にキャッシュされている場合、アプリケーションは最初のクエリをスキップします。キャッシュでIDが123、567、または908の投稿を見つけた場合は、IN()リストから削除できます。クエリキャッシュもこの戦略の恩恵を受ける可能性があります。1つのテーブルのみが頻繁に変更される場合、結合を分解するとキャッシュの無効化の数を減らすことができます。
  • クエリを個別に実行すると、ロックの競合を減らすことができる場合があります
  • アプリケーションで結合を行うと、異なるサーバーにテーブルを配置することでデータベースを簡単に拡張できます。
  • クエリ自体はより効率的です。この例でIN()は、結合の代わりにリストを使用すると、MySQLが行IDをソートし、結合で可能な場合よりも最適に行を取得できます。
  • 冗長な行アクセスを減らすことができます。アプリケーションで結合を行うことは、各行を1回だけ取得することを意味します。一方、クエリでの結合は、基本的に同じデータに繰り返しアクセスする可能性がある非正規化です。同じ理由で、このような再構築により、ネットワークトラフィック全体とメモリ使用量も削減される可能性があります。
  • ある程度まで、この手法は、MySQLが結合を実行するために使用するネストされたループアルゴリズムの代わりに、ハッシュ結合を手動で実装すると見なすことができます。ハッシュ結合の方が効率的かもしれません。

その結果、以前のクエリから大量のデータをキャッシュして再利用したり、複数のサーバーにデータを分散しIN()たり、結合をリストに置き換えたり、結合が同じテーブルを複数回参照したりすると、アプリケーションでの結合の実行がより効率的になります。

観察

InnoDBはクエリキャッシュをクロスチェックするときに少し手間がかかるため、最初の箇条書きが気に入っています。

最後の箇条書きについては、ネストループアルゴリズムを説明する投稿を2013年3月11日に投稿しました(JOIN条件とWHERE条件の実行に違いはありますか?)。これを読んだ後、結合分解がどれほど優れているかがわかります。

この本の他のすべての点に関しては、開発者は最終結果としてパフォーマンスを本当に探しています。高速ディスクの使用、CPU /コアの追加、ストレージエンジンの調整、構成ファイルの調整など、パフォーマンスの向上を外部の手段(アプリケーションの外部)に依存しているものもあります。他の人は座屈してより良いコードを書くでしょう。ストアドプロシージャですべてのビジネスインテリジェンスをコーディングすることに頼る人もいるかもしれませんが、それでも結合分解は適用しません(データベースレイヤーにアプリケーションロジックを配置することに対する、または他の投稿と一緒の議論を参照)。それはすべて、各デベロッパーショップの文化と寛容次第です。

一部のユーザーはパフォーマンスに満足しており、コードに触れない場合があります。他の人は、彼らが作曲に参加しようとする場合に得ることができる大きな利点があることに単に気づかない。

喜んでいる開発者のために...

試してみる !!!


3
3つのクエリへの変更に関するリンクについては、Baron、Vadim、Peterを知っていますが、この誤解を招く提案に同意しません。分裂を支持する議論のほとんどは、言及する価値がないほどまれです。JOINを使用して1つのクエリに固執し、それから改善に取り掛かりましょう。
リックジェームズ

2
@RickJamesあなたのコメントの精神に同意します。何年もの間、一部の人は結合分解の仕事をし、他の人は失敗するのを見てきました。適切なSQLスキルセットを使用していても、結合分解が正しく行われないと、うまくいかない可能性があります。私の現在の雇用者では、多くの部門がスケールアップとスケールアウトを好んでいます。特にレガシーコードが関係していて、深いポケットが利用できるときは。キャビアの味はあるが卵サラダの予算がある人にとって、結合分解はリスクに見合うだけの価値があるかもしれませんが、正しく行わなければなりません。
RolandoMySQLDBA

権利と時間があれば、Oracle環境でこれがどのように機能するかを確認したいと思います。
リックヘンダーソン

より速くできるもう1つの方法は、順序付けを行う場合、1つの大きなリストを順序付けるよりも小さいリストを順序付ける方が全体的に少ない計算になることです。
エヴァンシロキー

24

Postgres(とおそらく同程度に任意のRDBMSは、より少ない程度にMySQLが)、少数のクエリは、ほとんど常にずっと速いです。

複数のクエリの解析と計画のオーバーヘッドは、ほとんどの場合、すでに得られる利益よりも多くなっています。

クライアントで行われる追加の作業は言うまでもなく、結果を結合します。これは通常、それよりはるかに遅いです。RDBMSは、その種のタスクと操作に特化しており、元のデータ型に基づいています。text中間結果をキャストしたり戻したりすることも、クライアントのネイティブタイプに変換することもありません。これにより、結果が正しくない(または誤っている)ことさえあります。浮動小数点数を考える...

また、DBサーバーとクライアント間でより多くのデータを転送します。これは、値でいっぱいの手にとっては無視できる場合もあれば、大きな違いをもたらす場合もあります。

複数のクエリがデータベースサーバーへの複数のラウンドトリップを意味する場合は、ネットワークレイテンシとトランザクションオーバーヘッド、場合によっては接続オーバーヘッドも複数回収集します。大きな、大きな損失。

設定によっては、ネットワーク遅延だけで他のすべてよりも桁違いに長くかかる場合があります。

SOに関する関連質問:

トランザクションは途中でDB行のロックを収集するため、非常に大きく長時間実行されるクエリはターニングポイントがあります。非常に大きなクエリは、長時間にわたって多くのロックを保持し、同時クエリとの摩擦を引き起こす可能性があります。


好奇心から、あなたは非常に大きいと思いますか?
-Sablefoste

@Sablefoste:アクセスパターンに大きく依存します。重要なポイントは、同時トランザクションがキューを開始し、ロックが解放されるのを待機することです。または、リソースのかなりの部分を使用するのに十分なロックを蓄積した場合です。またはあなたのクエリは自動バキュームを妨害するほど長く実行する場合...
アーウィンBrandstetter

しかし、やや典型的な状況をとる場合、外部結合を使用して「親」テーブルの多くの冗長データを返すクエリは、アプリ(ほとんどの場合、ORMライブラリ)によって解析およびソートする必要があります。最初に必要なすべてのIDをフェッチし、次に外部結合ではなくIN()を使用して別の小さなselectをフェッチする小さなselect?2番目のアプローチはより効率的ではありませんか(DBとアプリの両方がCPUと通信帯域幅を消費することを考慮)?
JustAMartin

1
@JustAMartin:これは、RDBMSのクエリプランナーで処理した場合、ほぼ確実に高速になるクエリのように聞こえます-正しいクエリを想定しています。に関してreturns lots of redundant data for "parent" table:なぜ冗長データを返すのですか?必要なデータのみを返します。
アーウィンブランドステッター

1
外部結合では、RDBMSは結合された子ごとに複製された親テーブルからデータを返します。これは、ネットワークとメモリのオーバーヘッドを意味し、ORMツールで追加の解析を行って重複した親値を破棄し、n個の子を持つ1つの親のみを保持します。したがって、単一のクエリで、RDBMSクエリプランナーの効率的な作業を節約し、ネットワーク(またはローカルパイプ)リクエストを減らしますが、追加の不要なペイロードとORMライブラリ内のデータの移動は失われます。いつものように-最適化する前に測定してください。
-JustAMartin
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.