APIを作成し、動作させましたが、ユーザーから応答が遅いという苦情が寄せられています。コードを見てみると、すべてが正常に見えます。データベースは動作しており、クエリは単純ですが、応答時間はミリ秒単位ではなく秒単位です。よくある状況でしょう?
問題は、ほとんどの開発者がデータベース内のインデックス、アルゴリズムの複雑さ、データサイズなど、明白なものをチェックしていることです。しかし、実際のブレーキは、ほとんどの人が見ることのない場所に隠されていることがよくあります。APIが遅い12の明白でない理由を見てみましょう。

1. 外部呼び出しごとのDNSクエリ
APIは、決済システム、分析サービス、またはその他のマイクロサービスなどの外部サービスにアクセスします。そして、IPアドレスを知るために毎回DNSクエリを行います。これにより、各リクエストに20〜200ミリ秒が追加される可能性があります。
多くのHTTPクライアントは、デフォルトでDNSをキャッシュしません。API が 1 つのユーザーリクエストに対して 10 回の外部リクエストを行う場合、DNS だけで最大 2 秒を失います。
解決策:HTTPクライアントでDNSキャッシュを設定するか、接続を再利用して接続プーリングを使用します。
2.非効率的な形式でのデータのシリアル化
JSONは便利ですが、遅いです。APIがサービス間で大量のデータを転送する場合、JSONのシリアル化と逆シリアル化は、要求処理時間の大部分を占める可能性があります。
たとえば、10,000オブジェクトの配列をJSONにシリアル化するには、Pythonで50〜100ミリ秒かかる場合があります。これが 1 回のリクエストで複数回発生する場合、時間が累積されます。
代替案:サービス間の内部通信には、MessagePack、Protocol Buffers、または単純なバイナリ形式を試してください。これらは何倍も速く動作します。
3. 同期および冗長ログ
すべてのリクエスト、すべての応答、すべてのアクションをログに記録します。これはデバッグには適していますが、ログがファイルまたはデータベースに同期して書き込まれる場合、各書き込み操作はコードの実行をブロックします。
ファイルへの書き込みには 5 ~ 20 ミリ秒かかる場合があります。1 回のリクエストで 10 個のログを書き込むと、すでに 50〜200 ミリ秒の純遅延が発生します。
解決策:バッファを使用した非同期ログを使用し、ログを個別のプロセスまたはサービスに送信し、本番環境の詳細レベルを下げます。
4. データベースへの準備されたクエリがない
インデックスを使用しても、準備されたステートメントを使用する代わりに毎回新しいSQLクエリを送信すると、データベースの動作が遅くなる可能性があります。データベースは、毎回クエリを解析し、実行計画を作成してから実行う必要があります。
準備されたクエリはデータベースによってキャッシュされ、再実行はほぼ瞬時に行われます。簡単なクエリの実行時間を最大30〜40%節約できます。
5. クラウド機能のコールドスタート
サーバーレスアーキテクチャ(AWS Lambda、Google Cloud Functions)を使用している場合、コールドスタートにより、非アクティブ期間後の最初のリクエストに500ミリ秒から数秒の時間が追加される可能性があります。
多くの依存関係、大規模なライブラリ、長い初期化など、関数が重い場合、問題はさらに悪化します。コード自体は高速で動作しますが、ユーザーはブレーキを目にします。
解決策:重要な関数にプロビジョニングされた同時実行性を使用し、イメージのサイズを最適化し、ハンドラ関数の外で初期化を行います。
6. データベースとの接続プールがない
データベースへの新しい接続ごとに、TCP 接続の確立、認証、初期化に数十ミリ秒かかります。APIがリクエストごとに新しい接続を作成する場合、時間を無駄にしています。
接続プーリングは既存の接続を再利用します。これは基本的な最適化ですが、多くの初心者の開発者はそれを忘れたり、誤って設定したりします。
設定を確認してください。プール内の接続は十分ですか?接続が早く閉じられていませんか?タイムアウトは正しく設定されていますか?

7.すべてのリクエストに対してミドルウェアが実行されます
認証、ログ、CORS処理、検証のためのミドルウェアがあります。これはすべて、静的ファイルやヘルスチェックエンドポイントなど、すべてのリクエストに対して実行されます。
ミドルウェアがトークンを検証するためにデータベースまたは外部サービスにクエリを実行する場合、例外なくすべてのクエリに遅延が追加されます。
解決策:ミドルウェアの順序を最適化し、簡単なチェックを先に行い、不要なパスを除外し、トークン検証の結果をキャッシュします。
8. 重要な瞬間のガベージコレクション
自動メモリ管理言語(Python、Java、Go)は、定期的にガベージコレクタを実行します。ほとんどの場合、これは目立たないものの、APIがメモリ内に多くのオブジェクトを蓄積している場合、GCはリクエストの処理中に正しく機能し、数十または数百ミリ秒の実行を凍結することができます。
これは、GIL を使用する Python と、GC パラメータが正しく設定されていない Java で特に顕著です。
GCポーズを監視し、ガベージコレクタのパラメータを調整し、コードのホットスポットで不要なオブジェクトを作成しないようにします。
9. 非同期コードでのブロック操作
あなたは、async/await、FastAPI、またはNode.jsを使用していますが、コードのどこかで同期呼び出しを行っています。ファイルの読み取り、非同期ドライバーなしのデータベースへのクエリ、通常のリクエストを介したAPIへのアクセスなどです。
これによりイベントループがブロックされ、他のすべてのリクエストがキューに入れられます。1つの遅いクエリがサーバー全体を遅くします。
解決策:非同期ライブラリのみを使用し、スレッドプールまたは個別のプロセスにブロック操作を実行し、同期呼び出しの有無についてコード全体をチェックします。
10. 外部リクエストにタイムアウトがない
APIは外部サービスを呼び出しますが、タイムアウトは設定しません。外部サービスが遅くなったり応答しなくなったりすると、オペレーティングシステムのデフォルトのタイムアウトが発生するまで、リクエストが数分間停止します。
ユーザーには読み込みが終わらないように見え、APIはリソースを消費しながら、返ってこない可能性のある応答を待ち続けます。
常に合理的なタイムアウトを設定してください。外部APIの場合は5〜10秒、内部サービスの場合は1〜2秒です。ユーザーを待たせるよりも、エラーを素早く返す方が良いでしょう。
11. 同一のリクエストの再処理
ユーザーがボタンを数回クリックしたか、フロントエンドがタイムアウト時に再試行を送信します。APIは同じリクエストを受け取り、データベースへのリクエスト、計算、メールの送信など、それぞれを正確に処理します。
これは時間がかかるだけでなく、データの重複や誤動作につながる可能性があります。
解決策: idempotency keysを使用し、最近のクエリの結果をキャッシュし、フロントエンドで再送信をブロックします。
12. メトリクスとモニタリング自体がブレーキをかける
皮肉なことに、パフォーマンスを測定するためのツール自体がブレーキの原因になる可能性があります。詳細なメトリクスの収集、各リクエストのトレース、モニタリングシステムへのデータ送信など、これらすべてにリソースが必要です。
メトリクスを収集しすぎたり、メトリクスを同時に送信したりすると、CPU 時間が消費され、遅延が発生します。
合理的に行動しましょう。必要なメトリクスのみを収集し、トレースにはサンプリングを使用し、データは非同期でバッチ処理で送信します。
次にすべきことは?
これで、APIが遅くなる可能性のある12の明白でない理由がわかりました。次のステップは体系的なチェックです。プロファイリングを追加し、リクエスト処理の各段階で時間を測定し、ボトルネックを見つけます。
覚えておいてください。パフォーマンスは一度限りの最適化ではなく、継続的なプロセスです。モニタリングし、測定し、改善してください。
コディック — これは単なるアプリではなく、プログラミングの世界におけるあなたの個人的な指導者です。シンプルな言葉で説明し、実践で知識を定着させるのを助け、成功に対してクールなアチーブメントを与えます🏅
そして、私たちはクールな テレグラムチャンネル どんな質問でも尋ねて、経験豊富な開発者から助けを得ることができるフレンドリーなコミュニティ。参加しよう!
