FastAPI + SQLAlchemy性能改善プレイブック: 遅いAPIを計測ベースで高速化する
FastAPIの初期実装は非常に快適です。しかし運用フェーズに入ると、次のような症状が出てきます。
- 一覧APIのレスポンスが急に遅くなる
- 同時接続が増えるとp95が跳ねる
- CPUは余っているのにタイムアウトが増える
- DB接続数が上限に張り付く
こうした問題の多くは「Pythonが遅い」のではなく、SQLAlchemyの使い方とDBアクセス設計 に起因します。
本記事では、FastAPI + SQLAlchemy + PostgreSQL構成を前提に、実際の改善手順を計測ベースで整理します。
1. 最初に測るべき指標
最適化は、体感ではなく数値で進めます。最低限、以下を可視化します。
- APIのp50/p95/p99レイテンシ
- エンドポイント別SQL発行回数
- 1リクエストあたりのDB滞在時間
- connection pool待ち時間
- slow query件数(200ms以上など)
OpenTelemetryやNew Relicを使っているなら、アプリspanとDB spanを必ず紐付けてください。これだけでボトルネック特定速度が上がります。
2. N+1問題を最優先で潰す
最も頻出するのがN+1です。例えばユーザー一覧でプロフィールを参照すると、ユーザー数分の追加クエリが発行されます。
2.1 悪い例
|
|
2.2 改善例(joinedload/selectinload)
|
|
joinedload と selectinload はデータ量で使い分けます。
- 1対1/少量:
joinedload - 1対多/件数多め:
selectinload
闇雲に joinedload を増やすと行爆発が起きるため、EXPLAINで確認しながら適用します。
3. SQLAlchemy 2.xスタイルへ揃える
旧query APIと新APIが混在すると可読性と最適化精度が落ちます。2.xスタイルへ統一しましょう。
|
|
この形式は、EXPLAIN の追跡や再利用がしやすく、レビュー品質も上がります。
4. 必要な列だけ取る(過剰フェッチの削減)
ORMは便利ですが、何も考えずモデル全体を取ると不要データまで転送されます。特にJSONカラムやTEXTが重い場合、ここが効きます。
|
|
一覧APIはDTO用の軽量SELECTを使い、詳細APIでのみ重いカラムを取得する設計が安定します。
5. 接続プール設定を環境に合わせる
pool_size を適当に増やすだけでは逆効果です。PostgreSQL側上限とアプリ台数を合わせて設計します。
|
|
設計の目安:
- DB max_connections = 300
- API Pod = 6
- 1 Podあたりpool_size 20
この時点で理論最大120接続。バッチや管理接続も見込み、余白を残すのが安全です。
6. トランザクション境界を短くする
長いトランザクションはロック競合とスループット低下を招きます。
悪い例:
- DB更新
- 外部API呼び出し
- メール送信
- commit
この順は危険です。外部I/Oをトランザクション外へ逃がします。
改善例:
- DB更新 + commit
- 外部通知は非同期ジョブで実行
これだけで同時処理性能が目に見えて改善します。
7. インデックス設計をAPI単位で見直す
「インデックスはある」だけでは不足です。実際のWHERE + ORDER BYに合っているかが重要です。
例: 注文履歴API
|
|
この場合、次の複合indexが有効です。
|
|
単独indexを乱立させるより、アクセスパターンに合わせた複合indexを厳選した方が効きます。
8. キャッシュ導入は「遅い理由の解決後」に行う
キャッシュは万能ではありません。N+1やスロークエリを放置したまま載せると、整合性事故の温床になります。
導入順序:
- SQL最適化
- 接続プール調整
- 必要なエンドポイントに限定してRedis cache
キャッシュキーは resource:id:version 形式にし、更新時の無効化戦略を先に定義してください。
9. 負荷試験シナリオ(k6例)
最適化の成果は負荷試験で確認します。最低3シナリオが必要です。
- steady: 通常トラフィック
- burst: 短時間ピーク
- soak: 長時間連続実行(リーク検知)
|
|
改善前後で p95, SQL回数, DB CPU を比較し、定量で判断します。
10. 本番での改善手順テンプレート
- ボトルネックendpointの特定
- SQLログとEXPLAIN ANALYZE取得
- N+1解消
- 必要列取得へ変更
- インデックス追加(CONCURRENTLY)
- pool設定調整
- 負荷試験再実施
- 段階リリース(10%→50%→100%)
この手順を運用チーム全体でテンプレ化すると、パフォーマンス問題への対応速度が上がります。
まとめ
FastAPI + SQLAlchemyの性能改善は、派手なテクニックより 計測→原因分離→小さく改善 の積み重ねが効きます。
- N+1解消
- 過剰フェッチ削減
- 接続プール最適化
- インデックスの再設計
- 負荷試験で再検証
この5点を回せば、遅いAPIは高確率で改善できます。まずは「1リクエストあたりのSQL発行数」を可視化するところから始めるのが最短です。
付録: 改善施策の優先順位(最短で効く順)
時間が限られる現場では、まず「SQL発行回数削減 → インデックス最適化 → connection pool調整」の順で着手すると効果が出やすいです。特に、N+1解消だけでp95が半減するケースは珍しくありません。改善後は必ず同一負荷条件で再計測し、数字で効果を残しておくと、次の改善投資判断が通りやすくなります。