Web サービスのバックエンドの最適化は常にパフォーマンスの向上を目的としており、その重要な側面はデータ処理の高速化です。このプロセスには、リソースをより効率的に使用し、リクエストに対するシステムの応答時間を最小限に抑えることを目的とした多くの重要な改善が含まれています。この記事では、Web サービスを大幅に高速化できる実証済みのテクニックをいくつか紹介します。
多くのプログラマーは、アプリケーションの高速化を目指して、コードとアルゴリズムの最適化、適切なデータ構造と最適な操作の選択に重点を置いています。これは通常、パフォーマンスの向上につながり、最適化されたコードの速度は向上しますが、大幅には向上しません。
このわずかな増加は、メモリ操作の固有の速度によるものであり、元のコードが非常に非効率的でない限り、大幅な改善は期待できません。ただし、時間のかかる特定の操作、特に入出力操作を優先して最適化する必要があります。
ファイルを操作する場合でも、データベースと対話する場合でも、これらのタスクの実行時間は、メモリ内操作と比較すると常に顕著になります。ファイルからデータを読み取るプロセスに大きな影響を与えることはできませんが、データベースの操作は直接制御できます。開発者は、このインタラクションを大幅に改善するためのあらゆる機能を備えています。
データベースの操作をより効率的にし、バックエンド サービスのパフォーマンスを大幅に向上させるための次の戦略を検討してみましょう。
現在、データベースの対話にオブジェクト リレーショナル マッピング (ORM) システムを利用していないバックエンド Web サービスを見つけることはほとんどありません。最高の結果を目指している場合は、ORM のカスタマイズを検討してください。 ORM は効率的でエラーがありませんが、一般的な使用を目的として設計されています。この幅広い適用性は、多くの場合、高いパフォーマンスを犠牲にします。
ORM はさまざまなデータベースと互換性があるように作成されているため、プロジェクト用に選択したデータベースの特定の利点が失われる可能性があることに注意してください。たとえば、ここに示すように、独自のデータベース機能を活用すると、データベースの対話速度を最大 30 倍まで大幅に向上させることができます。
ORM によって提供されるデフォルトのクエリだけに依存するのではなく、最適化された独自のクエリを作成する価値があります。カスタム クエリは、特に複数の結合が含まれるシナリオでパフォーマンスが向上することがよくあります。
以下は、Spring JPA で結合クエリを使用してパフォーマンスを向上させる方法の簡単な例です。
@Transactional @Lock(LockModeType.PESSIMISTIC_READ) @Query(value = """ SELECT e FROM EmployeeRecord e LEFT JOIN DepartmentRecord d ON e.departmentId = d.id WHERE e.departmentId = :departmentId; """) List<EmployeeRecord> findEmployeesByDepartmentId(Integer departmentId);
ネストされたオブジェクトや深い階層システムを持つ複雑なクラスを使用すると、システムのパフォーマンスが大幅に低下する可能性があります。多くの場合、特に構造内のすべてのクラスが完全に利用されていない場合には、入れ子構造全体についてデータベースにクエリを実行する必要はありません。
遅延初期化は、ネストされたオブジェクトに対する不必要なクエリを軽減するのに役立ちますが、ネストされたオブジェクトが必要であっても、そのすべてのデータが必要なわけではない場合に問題が発生します。このジレンマの解決策は、フラット データ クラスを採用することです。
データベースから必要なフィールド データのみを収集するように設計されたクラスを作成する必要があります。次に、必要なすべての結合を組み込んだカスタム データベース クエリを使用して、本当に必要なフィールドのみを選択します。
このアプローチにより、クエリ速度が向上するだけでなく、データベースからサービスへのデータ トラフィックも削減されます。
たとえば、Spring JPA の NamedParameterJdbcTemplate を使用すると、必要なフィールドを持つフラット クラスを作成できます。
public record EmployeeDepartment(Integer employeeId, String employeeName, String departmentName) { }
次に、簡単なスクリプトを使用して、メイン テーブルと結合テーブルから必要なフィールドのみが収集されます。
public List<EmployeeDepartment> employeeDepartments() { return template.query(""" SELECT employees.employee_id, employees.employee_name, departments.department_name FROM employees LEFT JOIN departments ON employees.department_id = departments.department_id; """, new MapSqlParameterSource(), employeeDepartmentMapper); }
このアプローチにより、負荷が大幅に軽減され、データの操作がより効率的になります。
データを扱う際の次の重要なステップは、データ タイプを定義することです。主なタイプはホット データです。
ホット データは、サービスがリアルタイムで処理するデータです。 Web サービスの応答の関連性はその即時の応答性に依存するため、このデータはキャッシュできません。したがって、このデータは常に最新である必要があります。このサービスはホット データと一貫して動作し、新しい値を継続的に記録し、タイムリーな更新のために情報を抽出します。
ホット データをできるだけ効率的に処理するには、ホット データが格納されているテーブルをできるだけコンパクトに保つことが重要です。
列をできるだけ少なくする
テーブルには、アクティブに使用されるフィールドのみを含め、他のすべてのデータを別のテーブルに格納し、関連する行の ID のみを保持する必要があります。このアプローチにより、レポート目的などで必要なときに、このデータでメイン テーブルに過度の負担をかけることなく、すべての未使用フィールドにアクセスできます。
行数をできるだけ少なくする
不要になった行は保存しないでください。代わりに、それらをアーカイブ テーブルに移動します。このアプローチにより、すべての履歴データをアーカイブに保存しながら、クエリで必要な行をより速く見つけることができます。単純なジョブでこのプロセスを自動化すると、データのアーカイブへの関与が最小限に抑えられます。
インデックスを常に最新の状態に保つ
忘れずにインデックスを作成してください。インデックスは迅速なデータ検索に不可欠ですが、プログラマーによって見落とされがちです。適切なインデックス作成により、データベースの検索時間とメモリ消費量を大幅に削減できます。結合に関係する条件と列の両方に対して、複合インデックスを含むインデックスを必ず構築してください。
外部キーの使用をやめる
外部キーを使用すると、関連付けられたテーブルにキーが存在することを確認するためにデータベースに追加の負荷がかかり、特にデータの書き込み時にデータ操作が遅くなります。誤解しないでください。このようなテーブルに外部キーを格納することは可能であり、必要な場合もありますが、キーを単純にプレーンな値として格納することをお勧めします。
これらの簡単な方法により、テーブルの効率と実用性を最大限に高めることができます。
ウォーム データは、対応を準備するために使用されるデータですが、その関連性は重大な影響を及ぼしません。例には、製品の説明や利用可能なアクセサリのリストが含まれます。このようなデータを保存している間、テーブルのサイズを厳密に監視する必要はなくなりました。ただし、これらのテーブルは結合に頻繁に使用されるため、これらのテーブルでのインデックスの作成を見落とさないことが重要です。
ウォームデータをキャッシュする
Warm Data の主な利点は、そのキャッシュ可能性です。リクエストが行われると、データをメモリに保存できるため、データベース呼び出しの回数が減り、計算が高速化されます。ただし、キャッシュは定期的に更新する必要があることに注意してください。
適切な TTL (Time To Live) を設定する
適切に動作させるために、正しい Time To Live (TTL) を設定します。通常、ユーザーが Web サイトで決定を下して注文するまでにかかる平均時間に合わせて、TTL は約 90 秒で十分です。 TTL はサービスの要件に基づいて常に調整してください。
より小さなクラスを使用してウォーム データを保存する
キャッシュには、コンパクト クラスを使用します。本格的なクエリが実行され、テーブルからすべてのデータが収集される場合でも、すべてをキャッシュに保存することは避けてください。必要なデータだけを保存してください。このアプローチにより、バックエンド サービスのメモリ消費量が大幅に削減されます。
Warm Data のセットアップにはそれほど時間はかかりませんが、最終的には目に見える結果が得られます。
コールド データとは、ほとんど変更されないが、応答に必要なデータを指します。このようなデータの例には、店舗の名前や住所が含まれます。このデータはほとんど変更されず、応答の関連性への影響は最小限に抑えられます。
コールド データをキャッシュするかファイルに保存する
このデータ型は常にキャッシュする必要があります。このようなデータのサイズが大きいためにメモリに格納することが不可能な場合は、データをデータベースからアンロードし、すぐに使用できる形式でファイルに保存することを検討してください。カテゴリに分割し、最も頻繁に使用されるものだけを選択すると、メモリ使用量を削減できます。さらに、このアプローチではネットワーク経由で作業する必要がなくなるため、データベースからデータをフェッチする場合と比べて速度が大幅に向上します。
トリガーでキャッシュを更新
このようなキャッシュの存続時間 (TTL) は通常 24 時間に設定されます。キャッシュを更新し続けるには、タスクをスケジュールするか、このデータへの変更を監視してキャッシュの更新を開始するトリガーを作成する必要があります。たとえば、コールド データを投稿または更新するためにエンドポイントが呼び出された場合、キャッシュを更新するためにトリガーがアクティブ化される必要があります。
Cold Data を効果的に管理することも、応答効率を最適化し、システム全体のパフォーマンスを向上させる重要な部分です。
結論として、Web サービスのバックエンドの最適化は、コードとアルゴリズムの最適化だけに依存するわけではありません。データベースの対話を強化すると、サービスの効率と全体的なパフォーマンスが向上します。 ORM (オブジェクト リレーショナル マッピング) クエリの微調整、フラット データ クラスの利用、データ型の正確な定義、キャッシュ戦略の導入などの手法を実装すると、サービスのパフォーマンスを大幅に向上させることができます。これらの手段を通じて、Web サービスは最終的に効率、応答性の向上、および全体的な機能の強化を実現します。
主な手順: