負荷試験で「もう限界」をどの数字で判断するのか?
昨日も k6 や JMeter で本番相当の環境に負荷をかけ、秒間何リクエスト捌けるかを確認したことがあるはずだ。だがその数字がどこまで伸びれば安心で、どこから先が危険信号なのか、説明できるだろうか。
- スループット(処理数/秒)だけを見ても限界はわからない。リトルの法則によりレイテンシが伸びるだけでシステム内部の同時実行数は増える
- 「限界」は平均レイテンシではなく、p95・p99 のようなパーセンタイルが急激に悪化し始める地点(ノック点)で判断する
- ノック点を超えたら USE メソッド(使用率・飽和・エラー)で CPU やコネクションプールなど個々のリソースを調べ、何が飽和しているかを特定する
スループットが伸びているだけでは「まだ余裕がある」とは言えない
負荷試験でまず目に入る数字はスループット(throughput、単位時間あたりに処理できたリクエスト数。RPS や TPS とも呼ぶ)だろう。同時に投げるリクエストを増やしながらスループットが右肩上がりに伸びていれば、つい「まだ処理能力に余裕がある」と安心してしまう。
しかしスループットは必ずレイテンシ(1 リクエストあたりの応答時間)とセットで見る必要がある。この 2 つと「システム内で同時に処理中のリクエスト数」(同時実行数)は、リトルの法則(Little's Law)と呼ばれる関係式でつながっている。「同時実行数 ≒ スループット × レイテンシ」というシンプルな式で、レイテンシが 2 倍に伸びれば、スループットが変わらなくても同時実行数は 2 倍に膨らむ。レイテンシの悪化は、それだけでシステム内部の「詰まり具合」を悪化させるということだ。
レジの行列にたとえるとわかりやすい。客がまばらに来ている間は、レジ係一人の処理速度(スループット)で十分さばけて、待ち時間(レイテンシ)もほぼゼロだ。客足が増えても、レジ係がさばける最大人数までは行列は伸びず、処理数は比例して増えていく。しかし客がその最大処理数を超えて来店し続けると、レジ係の処理速度はそれ以上上がりようがなく頭打ちになり、代わりに行列がどんどん伸びて待ち時間だけが際限なく膨らんでいく。この「行列が伸び始める瞬間」こそが、負荷試験でいうノック点であり「もう限界」の実体だ。
平均ではなくパーセンタイルで見る理由
レイテンシを「平均 ◯ms」で報告すると、危険な兆候を見逃す。平均は極端に遅い一部のリクエストを、大多数の速いリクエストで薄めてしまうからだ。そこで使うのがパーセンタイル(percentile)という指標で、p95 なら「リクエストの 95% がこの時間以内に完了した」ことを、p99 なら「99% がこの時間以内」であることを意味する。
k6・JMeter・Gatling といった負荷試験ツールはいずれもレイテンシを p50・p90・p95・p99 のようなパーセンタイルで出力する。実務での合否基準も「p95 が 200ms 未満」「p99 が 1 秒未満」のように、パーセンタイルに閾値を置く形で決めることが多い。平均が安定していても p99 だけが跳ね始めているなら、一部のリクエストがどこかの処理待ちの列に並び始めている合図である。
「限界」の正体 ―― スループットの頭打ちとレイテンシの急上昇
同時接続数を少しずつ増やしながら負荷をかけていくと、多くのシステムは共通のパターンをたどる。最初はスループットもレイテンシもほぼ横ばいで伸びるが、ある地点を境にスループットの伸びが止まり(頭打ち)、レイテンシ、特に p99 のような裾の指標が急激に悪化し始める。この折れ曲がる地点はノック点(knee point)と呼ばれ、負荷試験で「もう限界」と判断する実務上の基準点になる。
同時接続数 ──▶ 増加させていく
スループット :増加 → 増加 → 頭打ち
p99 レイテンシ :横這 → 微増 → 急上昇
▲
ここが「限界」
ノック点を超えたリクエストは処理されないわけではなく、システム内のどこかの待ち行列(キュー)に積み上がって順番を待つ。だからスループットは天井に張り付いたまま、レイテンシだけが際限なく伸びていく。
内部の何が飽和しているのかを USE メソッドで特定する
ノック点を超えたことがわかったら、次は「システム内のどのリソースが詰まっているのか」を特定する番だ。ここで使えるのが USE メソッド(Utilization・Saturation・Errors)という考え方で、CPU・メモリ・ディスク I/O・コネクションプールといったリソースひとつひとつについて、使用率(utilization)・飽和(saturation、処理しきれず溜まっている待ち行列の量)・エラー(errors)の 3 つを確認していく。
たとえば CPU 使用率がまだ 50% でも、DB のコネクションプールが枯渇していれば、新しいリクエストはコネクションが空くまで待たされる。これも立派な「飽和」であり、CPU のグラフだけを見ていては見つけられない。ノック点でスループットが頭打ちになったなら、その裏では必ずどこかのリソースが飽和しているはずで、USE メソッドはその犯人を探すための地図になる。
本番で p99 レイテンシやエラー率が急上昇したときの一次切り分けは、まず CPU 使用率のグラフを見ることが多い。だがそこがまだ余裕を示している場合、次に疑うべきは DB のコネクションプールやスレッドプール、キューの滞留といった「見えにくい飽和」だ。APM ツールでコネクションプールの待機数やキューの長さを可視化しておくと、CPU が原因でない詰まりにも早く気づける。負荷試験の段階からこれらの指標も一緒に記録しておくと、本番障害時の切り分けが格段に速くなる。
k6 で同時接続数を段階的に増やし、スループットとレイテンシがどう変化するかを観察する。k6 チームが負荷試験の練習用に公開しているテストサイトに対してかける。
brew install k6
cat <<'EOF' > loadtest.js
import http from 'k6/http';
export const options = {
stages: [
{ duration: '20s', target: 10 },
{ duration: '20s', target: 50 },
{ duration: '20s', target: 150 },
],
};
export default function () {
http.get('https://test.k6.io/');
}
EOF
k6 run loadtest.js
実行が終わると summary に http_req_duration の avg / p(90) / p(95) が表示される。target を 10 → 50 → 150 と引き上げるにつれて p(95) がどれだけ増えるかを見れば、そこにノック点があるかどうかの手がかりになる。target をさらに引き上げて繰り返し実行すると、どこかで p(95) だけが跳ね上がり始める地点が見えてくるはずだ。
- スループットが頭打ちにならず伸び続けている限りは余裕がある — レイテンシも同時に伸びていれば、リトルの法則により同時実行数も一緒に増えているだけで、裏側の待ち行列はすでに伸び始めているかもしれない。スループットと p99 は必ずセットで見る。
- 平均レイテンシが安定していれば問題ない — 平均は一部の遅いリクエストを大多数の速いリクエストで薄めてしまう。体感の悪化は先に p95・p99 のようなパーセンタイルに現れる。
- CPU 使用率が 100% になっていなければまだ余裕がある — 飽和は CPU だけでなくコネクションプールやキューの待ち行列にも起こる。CPU が 50% でもコネクションプールが枯渇していればリクエストは詰まる。
- スループット
- 単位時間あたりに処理できたリクエスト数。RPS・TPS とも呼ばれる。
- パーセンタイル(p95・p99)
- p95 なら「リクエストの 95% がその時間以内に完了した」ことを示す指標。平均より遅い側の実態を捉えられる。
- リトルの法則
- 同時実行数 ≒ スループット × レイテンシ、という 3 者の関係を表す式。
- ノック点(knee point)
- 負荷を上げてもスループットが頭打ちになり、レイテンシだけが急上昇し始める地点。実務上の「限界」の目安。
- USE メソッド
- CPU やコネクションプールなど各リソースを使用率(Utilization)・飽和(Saturation)・エラー(Errors)の 3 観点で調べる分析手法。
- The USE Method | Brendan Gregg — USE メソッドの考案者本人による一次資料。使用率・飽和・エラーの定義と適用手順。
- Thresholds | Grafana k6 documentation — p95/p99 のようなパーセンタイルに対して合否基準を設定する公式リファレンス。
- What Is P99 Latency? | Aerospike — p99 レイテンシが平均と何が違い、なぜ重視すべきかをベンダーエンジニアリングブログが解説。