パフォーマンスのベストプラクティス - クエリ
多層アプリケーションでは多くの場合、データベースレベル、つまりクエリでボトルネックが発生します。外部のサーバーやリンクされたサーバーを使用する場合、これらのプラクティスの重要性はさらに高まります。データベースエンジンに関する専門知識があるとクエリを最適化するときに役立ちます。しかし、以下の一連のプラクティスに従うことで、アプリケーションのパフォーマンスの欠点を大幅にカバーすることが可能です。
リンクされた複数のサーバーにまたがる結合を実行しない
説明
複数のサーバーにまたがる結合は非常に非効率です。極力避けるようにします。
ソリューション
リンクされたサーバーを使用する場合、複数のサーバーにまたがる結合は避けます。
重要事項
リンクされたサーバー内のテーブルがデータベースサーバーに完全に読み込まれた後、結合が実行されます。
注意事項
外部エンティティとの内部結合を行うとクエリが遅くなる傾向があります。しかし、リンクされたサーバーで結合を行う必要がある場合は、追加ロジックを使用します。そうすることで、データのサブセットのみがリンクされたサーバーからプルされ、プルされたものが結合されるようにします(一時テーブル、OPENQUERYなど)。いずれにしても、リンクされた複数のサーバーにまたがる結合には注意を払って対応する必要があります。
データベースから取得するフィールドの数を最小限にする
説明
大きいRecords/RecordListsをアクションのパラメータとして渡すことは避けます。これを行うと、クエリされたすべてのフィールドをプラットフォームが取得することになります。
ソリューション
多くのアトリビュートを含むRecord/RecordListsを受信または出力するアクションを処理する場合は、それらのレコードのエンティティの代わりにストラクチャを使用することを検討します。ストラクチャを使用するとシステム全体を流れるデータの量が絞り込まれ、大きなメリットがもたらされます。
重要事項
データベースから取得するデータのサイズを最小限にし、クエリの低速化と過剰なメモリの使用を防ぐため、使用するクエリ結果のレコードのフィールドをコンパイラオプティマイザが自動的に検出します。使用しないフィールドは不要であるため、データベースから取得しません。しかし、クエリ結果のレコードをアクションの引数として使用する場合、オプティマイザには十分な情報がなく、使用するフィールドと使用しないフィールドを特定できません。この場合、すべてのフィールドをデータベースから取得し、場合によっては、データ量が膨大になります。
注意事項
メンテナンス性とパフォーマンスのバランスを考慮する必要があります。エンティティレコードをクエリ結果や入出力パラメータとして使用すると、通常は再利用性が高まり、コンポーネントが変更しやすくなります。このベストプラクティスは、フィールド数が多いエンティティ/クエリ結果を処理する場合にのみ適用してください。
最大レコード数は常に必要数に合わせる
説明
AggregateのMax Recordsプロパティが、表示するデータの量と常に一致するようにします。
ソリューション
クエリから取得するレコードの量に制限がある場合、それに応じてAggregateのMax Recordsプロパティを入力し、クエリ実行時間を最適化する必要があります。これはテーブルレコード内や、Aggregateを使用して単一レコードを取得するときに特に役立ちます。
重要事項
通常、数千件のレコードを1つの画面に表示することはないため、これらをすべてデータベースから取得する必要はありません。表示される行のみを取得します。こうすることで画面の読み込み速度が向上します。
注意事項
SQL要素でMax Recordsプロパティを設定してもSQL文は変更されません。この制限はアプリケーションレベルで、データベースによって返される結果に対してのみ適用されます。Aggregateとは異なり、データベースレベルで結果をフィルタリングするには、SQL句をSQL要素の文に追加する必要があります(TOP <n>
など)。
一括処理ではSQLクエリを使用する
説明
大量のオペレーションを実行する場合は、Aggregateを含むforeachループではなくSQLクエリを使用します(より変化に対応できるはAggregateです)。また、SQLクエリでは更新や大量の削除もエンティティアクションより速く実行できます。複数のクエリを使用して同じ行を更新する代わりに、単一の文でできるだけ多くの行を更新するクエリを記述します。
ソリューション
- すべてのエンティティアトリビュートを取得する必要がない場合は、Get呼び出しをAggregateに置き換えてIdentifierパラメータを使用することを検討します。プラットフォームはクエリを最適化し、必要なアトリビュートを取得します。
- すべてのエンティティアトリビュートを更新する必要がない場合は、Update呼び出しをSQLクエリに置き換えてUPDATE SET文を使用し、必要なアトリビュートのみを設定することを検討します。プラットフォームはUPDATE SET文も使用しますが、常にすべてのアトリビュートを更新します。
- 複数のレコードを削除する必要がある場合は、For Eachの後に各レコードに対するDeleteオペレーションを続ける、単一のDeleteによるSQLクエリを使用します。
- Deleteの後にCreateを使用する代わりにUpdateを使用します。
- Selectの後にDeleteとCreateを使用する代わりにCreateOrUpdateを使用します。
- Aggregateの後に、各レコードに対して別の単純なクエリを使用してクエリ結果をfor-eachループで処理する代わりに、SQLクエリでサブクエリを使用します。
重要事項
データベースに対するオペレーションを実行すると、サーバーとの通信のラウンドトリップコストが常に発生します。実行するオペレーションの数が多い場合、ラウンドトリップコストの合計が著しく増加します。そのため、オペレーションの数の減らすことには大きなメリットがあります。また、細かいオペレーションを数多く行うよりもバッチ処理を行ったほうが、データベースの効率性が大幅に向上します。さらに、不要な情報を取得しないことにも大きなメリットがあります。
注意事項
これは非常に重要なベストプラクティスですが、経験不足のためにあまり実践されていません。開発者の多くはAggregate使用し、SQLクエリの使用は極力避ける傾向にあります。
分離されたAggregateの使用を避ける
説明
Aggregateを実行するアクションを作成しないようにします。クエリがアクション内で分離されている場合、取得するフィールド数をプラットフォームで最適化できません。
ソリューション
コンパイラオプティマイザは、使用するフィールドを自動的に検出します。しかし、出力がエンティティまたはレコードリストであるアクションを呼び出す場合、DBからエンティティ(レコードリストの場合は複数のエンティティ)をすべて取得します。
重要事項
これは、アクションがエンティティ/レコードリストのすべてのフィールドを読み取らない場合も該当します。これにより、不要なデータベースの過負荷やメモリの使用が発生します。
注意事項
これにより、コードの再利用ができなくなります。そのため、場合によっては(特に出力が大きくない場合は)類似したクエリを多く使用する代わりに、分離したクエリを使用するほうが適切です。
1回のみ繰り返し、クエリに対するインデクサ([ ])の使用を避ける
説明
クエリ結果に対して複数回の繰り返すことは好ましくないプラクティスです。これを行うと、クエリ結果がメモリ内にコピーされます。直接インデクサを使用する場合も同様です(query[2].value式など)。
ソリューション
サーバーメモリの使用を抑える必要がある場合は、複数の繰り返しの実行(for each、テーブル/リストレコード)を避けるようにします。2つ目の繰り返しを実行するために、別のクエリを実行するほうが適切です(注記: DBがボトルネックである場合、これは適用されません)。
重要事項
同じクエリで複数の繰り返しを実行する場合、クエリ結果は自動的にメモリ内に読み込まれます。クエリが大量の結果を返す場合、特にページの読み込みが頻繁な場合にメモリの消費が大幅に増加します。また、式に対してインデクサを使用すると、繰り返しの数とは無関係にメモリに読み込まれます。
注意事項
これは画面に表示されるリストにも適用されます。たとえば、クエリを1回繰り返してテーブルレコードに表示する場合、クエリがメモリ内にコピーされます。これは、テーブルレコードの描画によってクエリ結果が再度繰り返されるために発生します。
実行クエリ数を最小限にする
説明
実行クエリ数を最小限にします。多くの場合、複数ではなく単一のクエリの実行で、必要なすべてのデータを取得できます。
ソリューション
可能な限り、複数のAggregateを1つにグループ化し、不要なデータベースへのトリップを避けるようにします。for-eachループ内のクエリはパフォーマンス上の重大な問題の原因になることが多いため、特に注意します。
重要事項
単純なクエリであってもデータベースに接続する際はラウンドトリップコストが発生します。また、データの読み込みとクエリにかかる正味コストは、通常複数クエリではなく単一クエリで実行するほうが低くなります。
注意事項
実行クエリ数を最小限にするとパフォーマンスを大幅に向上できますが、一般的には可読性が犠牲になります。このため、必要なときにのみ最適化することが重要です。Service Centerのレポートをすべて使用して、ボトルネックをピンポイントで特定します。高速なクエリは低速なクエリに関するレポートには示されませんが、高速なクエリが頻繁に実行されると、それらの合計時間がその他のすべてのログ(画面のログ、Webサービスのログなど)に影響を及ぼすことに留意してください。
ReturnedRowCountを避ける(バージョン4.2以下)
説明
ReturnedRowCount値を調べるためにのみクエリを使用することは避けます。
ソリューション
代わりに、SQLクエリでcountを使用します。
重要事項
OutSystemsプラットフォームバージョン4.2以前では、ReturnedRowCountを使用するとクエリ出力がメモリに送られます。また、Max. Recordsアトリビュートが必須であるため、クエリが誤った値を返す場合がありました。
注意事項
これは大量の結果を返す可能性のあるクエリに関するトラブルに対してのみ有効です。
OutSystemsプラットフォームのバージョンが4.2以下の場合は、OutSystemsのテクニカルサポートに連絡してサポートを受けてください。