平均は正常なのにp99だけ遅い

「たまに遅い」の正体を平均に隠されずに突き止められる。テールが出る原因を5系統に分け、どの層で待たされているかをヒストグラムとスパンで確定させる手順が身につく。

応用トラブルシュートパフォーマンステールレイテンシ待ち行列観測最終更新: 2026-07-29
3つの要点
TL;DR
  1. 平均は外れ値を薄めるので、テールの調査に平均を使ってはいけない。分布(ヒストグラム)と、遅い個体そのもののトレースを見る。
  2. テールの発生源は5系統に収まる。待ち行列(使用率が高い)・停止時間(GC/コンパクション)・資源の枯渇(プール/FD)・外れ値の依存先(1台だけ遅い)・データ依存(特定のキーだけ重い)。
  3. 決定的な切り分けは「遅いリクエストは常に遅いか、ランダムに遅いか」。常に遅いならデータ依存、ランダムなら待ち行列か停止時間で、両者は時間相関の有無で分かれる。

「平均レスポンスタイムは 80ms で健全。でもユーザーからは時々すごく遅いと言われる」。この乖離は錯覚ではありません。平均は外れ値を薄めるように設計された指標だからです。1000 リクエストのうち 10 件が 3 秒かかっても、平均は 30ms しか動きません。一方その 10 件を踏んだユーザーにとって、そのサービスは「遅いサービス」です。

さらに厄介なのは、1ページが内部で 20 個の API を呼ぶような構成では、1リクエストの p99 が、ページ全体では高い確率で誰かが踏むことです。p99 が 1% の事故ではなく、体感上の常態になります。

まず平均を捨てて分布を見る

横にスクロール

平均から外れた遅い要求だけを条件付きで比較する症状から観測と仮説分岐、復旧判定までを示す図
症状を観測値で分岐し、次の確認と再発防止へつなげる診断順序を整理します。

最初にやるのは指標の入れ替えです。平均・最大・p99 の3つだけでは足りず、分布の形を見ます

分布の形読み取れること疑うべき系統
山が1つ、右に長い裾同じ処理が時々待たされている待ち行列・停止時間
山が2つに割れている(双峰)速い経路と遅い経路が混在キャッシュのヒット/ミス、データ依存
階段状に離散した山固定の待ち時間が加算されているタイムアウト後のリトライ、ポーリング間隔
特定の時刻だけ裾が伸びる周期的な何かと衝突バッチ処理、GC、コンパクション、cron

分布が双峰なら、遅い山だけを取り出して共通点を探すのが最短です。特定のユーザーID、特定のエンドポイント、特定のインスタンス——どれか1つに偏っていれば、そこで話が終わります。

複数インスタンスの分位点は足し算できない

インスタンスごとの p99 を平均して「全体の p99」とするのは誤りです。分位点は合成できません。3台の p99 がそれぞれ 100ms でも、全体の p99 が 100ms である保証はありません。全体の分位点が必要なら、分位点そのものではなくヒストグラム(バケットごとの件数)を集約してから計算します。

テールの発生源は5系統

1. 待ち行列(使用率が高い)

最も多い原因です。サービス時間が一定でも、使用率が上がると待ち時間は非線形に伸びます。待ち行列の基本形では、待ち時間は使用率 ρ に対して 1 / (1 - ρ) に比例して発散します。

ρ = 0.5  → 待ち時間は空いているときの  2倍
ρ = 0.8  →                            5倍
ρ = 0.9  →                           10倍
ρ = 0.95 →                           20倍

つまり使用率 90% で運用しているサーバーは、平均が健全に見えてもテールが10倍に伸びているのが正常な挙動です。CPU 使用率・スレッドプールの使用率・コネクションプールの使用率を、平均ではなくピークで確認してください。原理は待ち行列理論とテールレイテンシ、必要な並列度の求め方はリトルの法則で扱っています。

2. 停止時間(GC・コンパクション・チェックポイント)

プロセス全体が数十〜数百ミリ秒止まる事象です。特徴はその瞬間に処理中だった全リクエストが揃って遅くなること。分布では「特定の時刻だけ裾が伸びる」形になり、遅いリクエストに内容的な共通点がありません。

確認するのは GC ログ(停止時間と頻度)、データベースのコンパクションやチェックポイントの実行時刻、そしてそれらとテールの発生時刻の相関です。時刻で相関するかどうかが決め手になります。

3. 資源の枯渇(プール・ファイルディスクリプタ)

コネクションプールが空になると、リクエストはプールが空くまでキューで待たされます。この待ちはアプリのログには現れにくく、「DBは速いのにアプリは遅い」という矛盾した観測になります。プールの待機時間・待機キュー長を専用のメトリクスとして出していないと、事実上見えません。

症状: DBのスロークエリログには何も出ないのに、アプリのDB呼び出しが遅い
 → クエリ実行時間ではなく「プールからコネクションを得るまでの時間」を計測しているか確認する

4. 外れ値の依存先(1台だけ遅い)

バックエンドが N 台あり、1台だけがディスク故障や設定ミスで遅い場合、そこへ振り分けられたリクエストだけが遅くなります。分布は双峰になり、遅い側の共通点がインスタンスIDに現れます。ロードバランサが最少コネクション方式なら遅いノードを自然に避けますが、ラウンドロビンだと均等に踏み続けます(ロードバランシング可視化で挙動の差が見られます)。

対処はヘッジリクエスト(一定時間で応答が来なければ別ノードにも投げ、先に返ったほうを使う)や、遅いノードの自動切り離しです。

5. データ依存(特定のキーだけ重い)

特定のユーザーだけデータ量が桁違いに多い、特定のキーにアクセスが集中している(ホットキー)、インデックスが効かない条件が特定の入力でだけ発生する。特徴は同じリクエストが常に遅いことです。

決定的な切り分け: 常に遅いか、ランダムに遅いか

5系統を1つずつ試すより、最初に1回の判定で半分に割るほうが速く進みます。

遅いリクエストを1件特定し、まったく同じ内容で再実行する

  常に遅い(再現する)
    → データ依存。入力・データ量・クエリプランを見る(系統5)

  ランダムに遅い(ふつうは速い)
    → 時刻で相関するか?
        する   → 停止時間・周期処理(系統2)
        しない → 待ち行列・プール枯渇・遅いノード(系統1・3・4)
どの層で待たされたかは、スパンの隙間に出る

分散トレースを見るとき、注目すべきは長いスパンそのものではなくスパンとスパンの隙間です。親スパンが 3 秒なのに、子スパンの合計が 200ms しかないなら、残りの 2.8 秒はどの子でもない場所——スレッドプールの待ち、コネクション取得待ち、GC 停止——で消えています。この隙間は計測されていない待ちの存在を示す、最も雄弁な手がかりです。

再発を防ぐ

  • SLO はテールで定義する。 平均で SLO を置くと、この症状は永久に検知されません。
  • 使用率の上限を決めて運用する。 使用率 90% は「効率がよい」のではなく「テールが10倍」の状態です。逆算はフェルミ推定と数字感覚の手順が使えます。
  • 待ちを計測する。 実行時間だけでなく、プール取得待ち・キュー滞留時間を独立したメトリクスにします。計測されていない待ちは存在しないことにされます。
  • 分位点ではなくヒストグラムを集約する。 後から任意の分位点を正しく計算できます。

まとめ

  • 平均は外れ値を薄める指標なので、テールの調査には使わない。分布の形と、遅い個体そのものを見る。
  • テールの発生源は5系統——待ち行列・停止時間・資源の枯渇・外れ値の依存先・データ依存。
  • 最初の判定は「同じリクエストが常に遅いか、ランダムに遅いか」。常に遅いならデータ依存、ランダムなら時刻相関の有無でさらに二分する。
  • 分散トレースではスパンの隙間を見る。子スパンの合計と親の差が、計測されていない待ちの正体。
  • 使用率 90% でのテール10倍は異常ではなく待ち行列の normal な帰結。SLO はテールで定義し、待ちそのものを計測する。

トラブルシュート実戦の記事ガイド

平均は正常なのにp99だけ遅いを実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

トラブルシュート

比較で見る軸

難易度: advanced / カテゴリ: トラブルシュート実戦 / タグ数: 5

導入後に効く点

テールの発生源は5系統に収まる。待ち行列(使用率が高い)・停止時間(GC/コンパクション)・資源の枯渇(プール/FD)・外れ値の依存先(1台だけ遅い)・データ依存(特定のキーだけ重い)。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
advanced
カテゴリ
トラブルシュート実戦
タグ数
5

判断チェックリスト

  • 自社の用途が「トラブルシュート / パフォーマンス」に近いか確認する。
  • 強みである「平均は外れ値を薄めるので、テールの調査に平均を使ってはいけない。分布(ヒストグラム)と、遅い個体そのもののトレースを見る。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

トラブルシュートパフォーマンステールレイテンシ待ち行列観測