コードで「データベースリクエストが多すぎる」と判断されるのは何ですか?


17

これは私自身の議論であり、私の同僚の何人かは、私がここに来て、それについて一般的なコンセンサスがあるかどうかを確認すると思います。

基本的に、データベースコールに関する次の2つの意見があります。1。データベースコールの数を減らすために必要なすべてを取得するために、1つの大きなコールを行います。 DB呼び出し

これが特に効果を発揮するのは、一般的なコードです。Employeeクラスの例を使用しますが、これはかなり簡単です。

Employeeクラスに10個の値属性(名、姓、雇用など)があり、次に2つのクラス属性があります... 1はDepartmentクラスを指し、1つのスーパーバイザーは別のEmployeeオブジェクトを指しているとします。

考え方1では、従業員データと、Department属性とSupervisor属性を設定するために必要なフィールド、または少なくともこれらのサブオブジェクトから最も頻繁に使用されるフィールドを返す1つの呼び出しを行います。

考え方#2では、最初にEmployeeオブジェクトのみを設定し、次に実際に要求された場合にのみDepartmentおよびSupervisorオブジェクトのみを設定します。

2のスタンスは非常に単純です。要求のサイズと、それらの要求のいずれかが行われるたびにヒットする必要があるデータベースオブジェクトの数を最小限に抑えます。#1のスタンスは、適切に実装できたとしても、コードが複数の接続を作成しなければならないという事実が、Webサーバーとデータベース間の接続を減らすのではなく、より多くの負担を引き起こすということです。

この調査の背後にある原動力は、Webサーバーとデータベースサーバー間のトラフィック量が制御不能になっていることです。


7
私の経験では、これに対する「正しい答え」はありません。レイテンシとスループットの間にはバランスがあります。低レイテンシーは、多くの小さなリクエストまたは1つの大きなリクエストにさえ耐えることができます。ただし、大量のデータを一度に移動する方が、待ち時間の長いリンクの方が優れている傾向があります。それでも、高遅延構成でスループットが低い場合は、小さなチャンクをフェッチして応答性を向上させることをお勧めします。

3
おそらく、N + 1問題に関連stackoverflow.com/questions/97197/...
バレラKolupaev

@Valera:便宜上、その質問に投稿されたリンクがあります:realsolve.co.uk/site/tech/hib-tip-pitfall.php
name=

4
「ウェブサーバーとデータベースサーバー間のトラフィック量が制御不能になっています。」どういう意味ですか?実際の問題について具体的に説明していただけますか?パフォーマンスに問題がありますか?プロファイリングと測定を行いましたか?質問の一部として、実際の測定からの実際の結果を提供してください。それ以外の場合は、単に推測しています。
-S.ロット

回答:


8

この質問の背景にある推進力がトラフィックが多すぎる場合、頻繁に使用されるオブジェクトのキャッシュを検討しましたか?例:EmployeeおよびDepartmentおよびSupervisorオブジェクトを取得した後、キャッシュを追加して、近い将来再び要求された場合に既にキャッシュ内にあり、取得する必要がないようにすることをお勧めします再び。もちろん、キャッシュはめったに使用されないオブジェクトを期限切れにする必要があり、また、アプリケーションによって変更されてデータベースに保存されたオブジェクトを削除できる必要があります。

使用している言語とフレームワークに応じて、必要な機能の一部(またはほとんど)を実行できるキャッシュフレームワークが既に存在する場合があります。Javaを使用する場合は、Apache Commons-Cacheを調べることができます(しばらく使用していませんが、休止状態に見えますが、使用可能です。前回使用したときはかなりまともでした)。


3

初めて何かを書くときは、常に読みやすさと明瞭さを求めてください。必要に応じて、必要に応じてリファクタリングできます。負荷テストを実行してボトルネックを見つけます。多くの場合、問題の原因となっている呼び出しの数ではなく、不適切に記述された呼び出しです。

分類が多すぎるものについては、アプリケーションによって異なります。ほとんどのWebアプリケーションでは、30秒未満で十分です。ユーザーの期待について話します。


正しく書かれていないdb呼び出しとは何ですか?
nuエベレスト

3

質問は、特定のページに必要なデータを推測する必要があるという仮定に基づいているようです。そうではありません。単純なアプローチほど簡単ではありませんが、データベースを呼び出す前に部門またはスーパーバイザーの属性が必要かどうかを確認できるようにコードを設計できます。


3

これらは私が使用するルールであり、おそらくあなたに役立つでしょう。

  1. 最初に測定してください! そのリソースに流れるトラフィックを実際に見ることができ、そのリソースの応答が遅い場合を除き、「遅くなる可能性のある」コードも調べません。
  2. 1リクエスト= Kクエリ。データベースと通信する回数は、要求されたリソースの種類によって完全に決まります。要求の性質またはそのリソースの状態によるものではありません。あなたの例では、おそらく最大で3つのクエリです。1つは従業員、1つは部署、1つは監督者です。それぞれがいくつあるかは関係ありません。
  3. 使用しないものを照会しないでください。これが私たちが話しているHTTPである場合、後でデータを照会する意味はありません。後でありません。各リクエストはクリーンな状態から始まります。テーブルのほとんどの列が必要な場合もありますが、必要な場合は1つまたは2つだけです。必要なフィールドが正確にわかったら、それだけを要求します。
  4. 問題にハードウェアを投げます。 サーバーは安価です。データベースをより強力なボックスに移動するだけで、十分なパフォーマンスを得ることができる場合があります。またはいくつかのクエリを読み取り専用レプリカに送信します。
  5. 最初にキャッシュを無効にしてから、キャッシュを実装します。頻繁に使用されるデータまたはクエリが困難なデータをキャッシュに配置する衝動は強い。しかし、多くの場合、未使用のデータを削除したり、置き換えられたデータを期限切れにすることは見落とされます。キャッシュからデータを取り出す方法を知っている場合。その後、キャッシュに安全に配置します。クエリを実行するよりもキャッシュを無効にする方が費用がかかることが判明した場合は、その後、キャッシュは必要ありませんでした。

2

ここでの戦略はどちらも完全に有効です。それぞれに利点と欠点があります。

3つのオブジェクトすべてに対する1つの呼び出し:

  • より速く動作します
  • 必要な場合に必要なものを正確に取得します
  • おそらく1つの場合にのみ使用可能です(ただし、非常に一般的なケースかもしれません)
  • 維持がより困難になります
  • より頻繁に保守する必要があります(3つのオブジェクトのスキーマまたは必要なデータのいずれかが変更されると変更されるため)

オブジェクトごとに1回の呼び出し(合計3回の呼び出し)

  • 各オブジェクトタイプの単一のインスタンスを生成する汎用呼び出しを提供します。その後、それらは独立して使用できます
  • クエリ構造がより単純になるため、より保守しやすくなります。
  • 遅くなります(必ずしも3倍遅くなるとは限りませんが、同じデータのオーバーヘッドが増加します)
  • 不要なデータの取得で問題が発生する可能性があります(1つのフィールドが必要なときにレコード全体を取り出すのは無駄です)
  • 単一レコードクエリがコレクション内のレコードごとに1回、N回送信される場合、多対1の関係が存在する場合、N + 1の問題が発生する可能性があります。

いくつかの懸念事項(2番目のリストの3番目と5番目)に応えて...スーパーバイザーと部署が1/3(またはそれ以下)しか使用されない場合はどうなりますか?子を含むようにコーディングされたList <>オブジェクトが最初に参照されるとすぐに、すべての子を取得するようにコードが設計されている場合はどうなりますか?...これにより、ほとんどの警戒心が緩和されますか?
user107775

補助オブジェクトが必要になることがめったにない場合、一般的な場合、これは高速になります(取得するデータが少なくなります)が、最悪の場合は遅くなります(コンピューターからの通信オーバーヘッドの3倍を使用して、同じデータ以上を取得します)。N + 1の問題に関しては、オブジェクトのリストを取得するクエリを設計して、関係の「1」側への外部キーに基づいてそれを実行できるようにする必要があります。その後、複数の行をプルしますクエリ結果のうち。レコードの主キーが必要なバージョンのクエリを使用することはできません。
キース

1

私にとって、DB要求が多すぎると、いつでも必要なデータをロードするのに必要な数を超える要求を行っています。

ですから、データは必要ありません。メモリを無駄にせずに後で2回目の旅行を避ける必要があります。ただし、データ量が必要な場合は、dbへの呼び出しを最小限に抑える必要があります。

両方のオプションを用意し、状況に応じてそれぞれのオプションを使用します。

編集:これはもちろんあなたの状況にも依存することに留意してください。たとえば、WebAppの場合、WepAppのWeb全体ではなく、ネットワーク内のDBにアクセスするデスクトップアプリの場合とは異なる考慮事項が必要です。


共通のコードを書いていて、コードの使用方法がわからない場合はどうでしょう。たぶん、スーパーバイザーを必要としない誰かを想像することはないでしょうが、作業しているアプリケーションだけがそれを必要とすることがわかります。もちろん、別々の関数を記述することもできます。1つは含めないもの、もう1つは含めるものですが、どのコードで使用するには詳細な知識が必要になりますか?
user107775

@ user107775私は通常、それぞれの場合に2つの関数のみを記述します。プロパティ値のみを返すものと、関連するすべてのクラスを含むクラスを返すもの。これは、ほとんどの場合、プロパティのみが必要だからです。この方法では、詳細な知識は必要ありません。1つだけが基本的な知識を習得し、その他の知識はすべて必要です。私はそれが合理的なバランスだと思います。(ただし、特定のケースではより最適化が必要な場合がありますが、それはケースバイケースです)。
AJC

1

DBに接続し、リクエストを送信して解析するには、通常、結果の取得に比べてかなりの時間がかかります。そのため、全体的な傾向は、1つのリクエストでできるだけ多くのクエリを連結することです。

それでも、これをすべて一発で実行すると、コードが維持できなくなります。代わりに、通常、追加の抽象化レイヤーによって達成されます:コードは必要に応じて複数のリクエストをスケジュールし、エンジンはこれを1つの大きなリクエストとして解析し(おそらくキャッシュを途中で使用します)、必要に応じて応答をディスパッチします。

もちろん、必ずしもすべてを1つのクエリで取得できるとは限りません。次のクエリを作成するために必要なデータを提供するクエリがある場合が多いため、それを繰り返す必要があります。膨大な数のクエリのバンドルを実行して、できるだけ多くのクエリを一度に実行する方が、データベースへの数百回の小さなショットよりも優れています。

したがって、必要なものを計画し、それを要求して取得し、さらに必要な場合は再度要求して取得し、コンテンツの生成にデータを利用します。コード全体に散在するローカル変数の初期化のようなデータベースリクエストの使用は絶対に避けてください。


1

最適化が早すぎると判断した場合に、どの選択を行うべきかを知るためのアプリケーションについては十分に知りません。スーパーバイザーデータはどのくらいの頻度で使用されますか?無駄に思えるかもしれませんが、わかりません。それらを分離しておくと、システムを監視して、それらが一緒に使用される頻度を確認できる場合があります。1回の呼び出しでそれらを結合するだけの決定を下すことができます。それ以外の場合、この大きな呼びかけでボトルネックを作成し始めたら、どこでトラブルシューティングを始めますか?省略すべき意味を特定するのが難しい。このプロセスにさらにデータフィールドが追加される場合があります。

データベースのメモリとディスクの違いがどれだけあるのかを知るのは興味深いでしょう。住所と比較して、部門が多少なりとも変わる可能性があると感じさせるものは何もありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.