モジュール 09ターム3 · 信頼性とスケール16 ポスター項目

パフォーマンスとスケーリング

ログインすると、進捗を複数のデバイスで保存できます。ログインするまでは、このブラウザに保存されます。

なぜ重要なのか

プロダクトを殺す失敗は正反対の 2 種類です。ユーザーが 100 人になっただけで倒れるものを出してしまうこと、そしてまだいない 100 万人のユーザーのために作り込みすぎること。本モジュールでは、決まった順序に沿って まず計測し、それから最も安い修正を適用する 方法を学びます。

学習目標

  1. 推測ではなく、負荷テストとプロファイリングでボトルネックを見つけられる。
  2. スケーリングのはしごを正しい順序で登れる。
  3. invalidation(無効化)まで含めて、正しくキャッシュできる。
  4. ロードバランサー、リバースプロキシ、API ゲートウェイを理解している。
  5. サーバーレスの制限とコールドスタートを把握している。
  6. クラウドと AI のコストを第一級の指標として管理できる。

スケーリングのはしご(登るのは計測してから)

  1. コードを直す: インデックス、N+1、ページネーション、ペイロードサイズ(03 · データの基礎)。
  2. キャッシュする: 静的コンテンツには CDN、公開の動的コンテンツにはエッジキャッシュ、高コストな計算にはアプリケーションキャッシュ。
  3. 処理を非同期に移す(08 · 非同期処理、イベント、リアルタイム)。
  4. 垂直スケール(47): より大きなデータベース/サーバーにします。エンジニアリングの工数は少なく、思っている以上に遠くまで行けます。
  5. ステートレスなアプリ層を水平スケール(46) し、ロードバランサーの背後に置いてオートスケール(45)させます。
  6. 読み取りの多い負荷には リードレプリカ(29) を使います(レプリケーション遅延は受け入れます)。
  7. 大きなテーブルを パーティション(31) します(例: イベントやログを月単位で)。
  8. シャーディング(30) は、1 つのプライマリデータベースでは本当に対応できなくなったときだけです。ほとんどの企業はこれを必要としません。

レッスン

9.1 キャッシュ(2, 48, 49, 50)

  • 層: ブラウザ(Cache-Control)、静的アセット向けの CDN(48)、動的レスポンスの エッジキャッシュ(49)、アプリケーションキャッシュ(Redis)、DB のクエリキャッシュ。
  • パターン: cache-aside(アプリ経由で読み込む)、write-through、stale-while-revalidate。
  • invalidation(50): TTL(シンプルで、いずれ最新になる)、書き込み時の明示的なパージ(タグベースのパージ)、バージョン付きキー(user:42:v7)。
  • ユーザーごとのプライベートなデータを、共有キャッシュに共有キーで保存してはいけません(データ漏えいになります)。キーにユーザー/テナントを含めるか、Cache-Control: private を指定します。
  • スタンピードを防ぎます: リクエストの集約(coalescing)、jitter 付きの TTL、期限前の更新。
  • LLM 特有の対策: 長い静的プレフィックスにはプロバイダーのプロンプトキャッシュを使い、決定的な結果(埋め込み、分類)はキャッシュします。

9.2 入口のコンポーネント(3, 4, 5)

  • リバースプロキシ(4): TLS を終端し、圧縮し、遅いクライアントをバッファし、ルーティングします(Nginx、Caddy、Cloudflare、プラットフォームのエッジ)。
  • ロードバランサー(3): ヘルスチェックを使ってトラフィックをインスタンスに分散します。L4(TCP)と L7(HTTP)があります。スティッキーセッションは悪い兆候です。アプリサーバーはステートレスに保ちます。
  • API ゲートウェイ(5): 多数のサービスに対する認証、クォータ、rate limit、ルーティング、API キー。複数のサービスを持つようになったら、あるいは API アクセスを販売するようになったら役立ちます。

9.3 サーバーレスの現実(89, 90)

  • 制限: 最大実行時間、メモリ、ペイロードサイズ、同時実行数、そして DB コネクションへの圧迫。自分のプラットフォームの数値を把握しておいてください。
  • コールドスタート: アイドル後の最初のリクエストは遅くなります。バンドルを小さくする、軽量なランタイム(エッジ)を使う、レイテンシが重要な経路ではプロビジョンド/最小インスタンスを使う、といった方法で緩和します。
  • 長時間かかるエージェントの処理は、リクエストに縛られた関数の中で行うべきではありません。永続ジョブを使います。

9.4 コスト最適化(88)

  • アクティブユーザーあたりのコストと AI タスクあたりのコストを、プロダクト指標としてダッシュボードに載せます。
  • 効果が最も大きいもの: インスタンスの適正化、アイドルリソースの削除、LLM の結果のキャッシュ、単純なタスクをより小さく安いモデルに振り分けること、バッチ処理、ストレージのライフサイクルルール、egress の意識。
  • すべてのプロバイダーにハードな利用上限と異常アラートを設定します。コストの帰属がわかるよう、リソースにプロダクト/ベンチャー単位のタグを付けます。

9.5 負荷テストの進め方

シナリオを定義する(現実的なユーザージャーニー、思考時間、データ量)→ 段階的に負荷を上げる(smoke、load、stress、soak)→ p95/p99、エラー率、飽和度(CPU、メモリ、DB コネクション、キューの滞留数)を観察する → 最初に壊れるものを見つける → 直す → 繰り返す。

Lab 9: 崖を見つける

  1. メインのジャーニーについて k6 のシナリオを書き、エラー率が 1% を超えるか p95 が予算を超えるまで負荷を上げます。その「崖」を記録します。
  2. 最大のボトルネックをプロファイリングして直します。これを 2 回繰り返します。修正ごとのキャパシティ向上をグラフにします。
  3. 公開の動的ページを 1 つ選び、CDN とエッジキャッシュを追加します。更新時には正しく invalidation されるようにします。
  4. コストダッシュボードを作ります: アクティブユーザーあたりの月間インフラコストと AI コスト。

完了の定義

AI プロンプトパック

  • 「この k6 の出力と DB の統計情報をもとに、考えられるボトルネックを順位付けし、それぞれに最も安い修正を提案してください。」
  • 「このエンドポイントにキャッシュを追加してください。キャッシュキーの設計(テナントを含む)、TTL、書き込み時の invalidation、スタンピード対策を含めてください。」