性能テストは「速いかどうか」を見るテストではなく、「業務量から決めた目標(何件/秒を、何秒以内で、エラー何%以下で)を満たすか」を確かめるテストです。 目標は、平均ではなく 90・95パーセンタイル(遅い方から 10%・5% を除いたときの最大)で決めます。

手順 やること 決めるもの・出すもの
① 性能要件を決める 業務量から、ピーク時の件数・同時接続を計算する 目標スループット・応答時間(p90/p95)・エラー率
② 計画を立てる テストの種類・シナリオ・環境・データを決める 性能テスト計画書
③ 測る 負荷をかけ、応答時間とサーバーの資源を同時に記録する k6 などの結果・CPU/メモリ/DB の記録
④ 判定して報告する 合格基準と比べ、ボトルネックと限界を報告する 性能テスト報告書

実際に測ると、平均とパーセンタイルはこれだけ違いました。 同時に4件まで処理できるサーバー(1件約50ms、5% だけ約200ms)に、k6 で 40件/秒の負荷を 30秒かけた結果です。

指標 値 読み方
平均(avg) 62.55 ms 遅い 5% が平均に薄まって見えない
中央値(med) 51.69 ms 半分の要求はこれより速い
p90 64.39 ms 90% の要求はこれより速い
p95 186.23 ms 95% の要求はこれより速い(遅い 5% の入り口)
p99 239.71 ms 100件に1件はこれより遅い

確認環境:k6 v2.3.0(GitHub のリリースから入れ、チェックサムを照合)、Node.js 22.22.2 で作ったテスト用の HTTP サーバー、Python 3.11(Ubuntu 24.04)で測り、計算しました。図は mermaid-cli 11.14.0 で描画できることを確かめました。サーバーと k6 は同じマシンで動かしています。k6 の機能・テストの種類はGrafana k6 のドキュメント(v2.3.x)で確かめています(確認日 2026年10月13日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「性能テストの進め方|性能要件の決め方と合格基準のテンプレート」

① 性能要件の決め方:業務量から計算する

性能要件は「速いほどよい」ではなく、業務量から数字を出します。 根拠のない「3秒以内」は、テストで満たしても本番で足りるか分からず、満たせなくても直す量が決められません。

必要な数字と、どこから取るか

数字 例 どこから取るか
1日の業務の件数 注文 12,000件/日 業務の資料、今のシステムの DB の件数
ピークの1時間への集中 1日の 15% アクセスログの時間ごとの件数
1件の業務で呼ばれる API の回数 8回 画面の流れ(一覧→詳細→カート→注文…)を数える
1時間の中の偏り 3倍 1分ごとの最大 ÷ 1時間の平均(ログから)
将来の伸び 1.5倍 事業計画(何年先まで今の構成で持たせるか)
1人がいる時間 600秒 アクセスログのセッションの長さ

Python で計算する

# 業務量から性能目標を出す
orders_per_day   = 12_000   # 1日の注文件数(業務の資料から)
peak_hour_share  = 0.15     # 一番多い1時間に、1日の何割が集中するか(アクセスログから)
requests_per_ord = 8        # 1件の注文で呼ばれる API の回数(画面遷移から数える)
burst_factor     = 3        # 1時間の中の偏り(1分単位の最大 ÷ 平均)
growth           = 1.5      # 3年後の伸び
session_sec      = 600      # 1人がサイトにいる時間(秒)
resp_sec         = 0.3      # 目標の応答時間(秒)

peak_orders_h = orders_per_day * peak_hour_share
avg_rps       = peak_orders_h * requests_per_ord / 3600
target_rps    = avg_rps * burst_factor * growth
users_per_sec = peak_orders_h / 3600
concurrent_users = users_per_sec * growth * session_sec                  # リトルの法則 L = λW
inflight      = target_rps * resp_sec                                    # サーバーの中で同時に処理中の要求

print(f"ピーク1時間の注文      {peak_orders_h:,.0f} 件/時")
print(f"ピーク1時間の平均      {avg_rps:.1f} req/s")
print(f"目標スループット       {target_rps:.1f} req/s(偏り×{burst_factor}・伸び×{growth})")
print(f"同時に使っている人     {concurrent_users:,.0f} 人")
print(f"同時に処理中の要求     {inflight:.1f} 件")
ピーク1時間の注文      1,800 件/時
ピーク1時間の平均      4.0 req/s
目標スループット       18.0 req/s(偏り×3・伸び×1.5)
同時に使っている人     450 人
同時に処理中の要求     5.4 件

「同時接続数」は2つの意味で使われるので、分けて書きます。

言葉 意味 この例 計算
同時に使っている人(同時ユーザー) ログインして画面を見ている人 450人 1秒あたりに来る人 × いる時間(リトルの法則)
同時に処理中の要求 サーバーの中で処理している要求 5.4件 1秒あたりの要求 × 応答時間

「同時ユーザー 450人」は「450件の要求が同時に来る」ではありません。画面を読んでいる時間があるので、サーバーの中で同時に処理しているのは、この例では 5〜6件です。負荷テストのツールで「仮想ユーザー(VU)450」を設定するときは、待ち時間(think time)を入れないと、実際よりずっと重い負荷になります。 同時接続・スループットの見積もりは、関連記事「キャパシティ見積もりの計算方法|同時接続数・スループット・ディスク容量」で扱っています。

応答時間は平均ではなくパーセンタイルで決める

パーセンタイルは、速い順に並べて、その割合の位置にある値です。 p95 = 186ms なら、「95% の要求は 186ms 以内に返った」という意味です。

測った 40件/秒・801件の応答時間を、Python で並べ直した結果です。

件数 801
平均 58.2 ms / 中央値 50.7 ms
p90: 59.0 ms
p95: 150.1 ms
p99: 236.4 ms
200ms を超えた割合: 2.5%
k6 の要約: {'avg': 58.2, 'med': 50.7, 'p(90)': 59.0, 'p(95)': 150.1, 'p(99)': 236.4}

  0–50 ms  368件 ####################################
 50–60 ms  372件 #####################################
 60–100ms   18件 #
100–150ms    2件
150–200ms   21件 ##
200–250ms   20件 ##

Python で計算した値は、k6 の要約と一致しました。分布は 50ms 前後の山と、150〜250ms の小さな山の2つに分かれています。 平均(58.2ms)はどちらの山にも無い値で、「遅い 5% の人がどれだけ待ったか」は平均からは分かりません。

決め方 向いている場面
p90・p95 で決める 画面・API の応答時間(ほとんどの人の体感)
p99 も見る 決済・ログインなど、遅いと離脱や二重送信につながる処理
最大値 目標にはしにくい(1件の外れ値で変わる)。タイムアウトの設定と合わせて見る
平均 目標には使わない。スループットの計算(リトルの法則)に使う

p95 は、遅い要求の割合が 5% に近いと、測るたびに大きく変わります。 同じ 40件/秒の負荷で、30秒の1回目は 186ms、20秒の2回目は 174ms、3回目は 150ms でした。遅い要求がちょうど 5% 前後あり、p95 の位置がその境目にあるためです。目標の判定は、1回ではなく複数回・長めの時間で測って行います。

② テストの種類の比較

性能テストには目的の違う種類があり、計画書でどれを行うかを決めます。 名前と中身は、Grafana k6 のドキュメントの分け方をもとにしています。

種類 目的 負荷のかけ方 時間の目安(k6 ドキュメント)
スモーク スクリプトとシステムが最小の負荷で動くか ごく少ない負荷 数秒〜数分
負荷(平均負荷) 想定の普段・ピークの負荷で、目標を満たすか 目標のスループット 5〜60分
ストレス 想定を超えたときにどうなるか 目標の 1.5〜2倍など 5〜60分
耐久(ソーク) 長時間で性能が落ちないか(メモリリーク・接続のもれ) 普段の負荷を長く 数時間
スパイク 急な短い集中に耐えて、元に戻るか 急に何倍にもする 数分
限界(ブレークポイント) どこまで耐えられるかを探す 少しずつ上げ続ける 必要なだけ
種類 合格かどうかの見方 よく見つかる問題
負荷 目標の p90/p95・エラー率を満たすか 遅い SQL、N+1、キャッシュが効いていない
ストレス エラーにならず遅くなるだけか、どこから崩れるか 接続プールの枯渇、タイムアウトの連鎖
耐久 始めと終わりで p95・メモリが変わらないか メモリリーク、ログ・一時ファイルの増加
スパイク 集中が去ったあと、何秒で元の応答時間に戻るか 待ち行列が捌けない、オートスケールの遅れ
限界 何件/秒で頭打ちになるか 一番弱い資源(CPU・DB・外部 API)

実測:限界の見つけ方(ストレス・限界テスト)

負荷を段階的に上げて、スループットが増えなくなる点(限界)を探します。 同時に4件までしか処理できないテスト用サーバーに、20〜90件/秒をそれぞれ 20秒かけた結果です。

かけた負荷(件/秒) 実際に処理した件/秒 p95 処理しきれず落とした数
20 20.0 60.2 ms 0
40 39.9 174.4 ms 0
60 59.9 160.9 ms 0
70 69.1 453.2 ms 0
75 68.7 1,245.3 ms 41
80 70.3 1,663.4 ms 73
90 69.2 3,124.1 ms 187
xychart-beta
    title "かけた負荷と p95(ms)"
    x-axis "かけた負荷(件/秒)" [20, 40, 60, 70, 75, 80, 90]
    y-axis "p95(ms)" 0 --> 3500
    line [60, 174, 161, 453, 1245, 1663, 3124]

処理できる量は約70件/秒で頭打ちになり、それ以上の負荷は「待ち時間」になりました。 エラー率は最後まで 0% でしたが、p95 は 60件/秒の 161ms から、90件/秒では 3.1秒になっています。エラーが出ないから大丈夫、とは判断できません。 応答時間とスループットを並べて見ます。

この上限は計算とも合います。同時に処理できる数 4 ÷ 1件の平均処理時間 0.0575秒(95% が 50ms、5% が 200ms)= 約69.6件/秒 です。本番のシステムでは、この「同時に処理できる数」が、スレッド数・DB の接続プール・CPU のコア数などで決まります。

「落とした数」は k6 の dropped_iterations です。 k6 のドキュメントでは、到着率を決める実行方法(constant-arrival-rate など)で、空いている仮想ユーザーが無いと、その回の要求は送られずに落とされます。テストの始めに出るなら仮想ユーザーの不足、途中から出るならシステムが遅くなったことが原因です。この表では 75件/秒から出ていて、サーバーが遅くなったことを示しています。

実測:スパイクからの戻り

普段 30件/秒のところに、5秒で 120件/秒まで上げて 10秒続け、また 30件/秒に戻しました。応答が返った時刻で、5秒ごとに区切った p95 です。

経過(秒) 処理した件/秒 p95
0〜20 28〜30 59〜62 ms
20〜25(急増) 60 490.6 ms
25〜45(ピーク〜下げ) 68〜72 1,788〜4,394 ms
45〜50 42 1,762.0 ms
50〜55 30 161.4 ms
55〜70 30 60〜100 ms

負荷を戻し始めてから(35秒)、応答時間がほぼ元に戻るまで(55秒)約20秒かかりました。 たまった待ち行列を処理する時間です。スパイクテストでは「耐えたか」に加えて、「何秒で戻ったか」を合格基準に入れます。 この実行では、p95 が全体で 4.34秒になり、閾値(p95 < 300ms)を満たせずに k6 は終了コード 99 で終わりました。

性能テスト計画書の項目

項目 書くこと 例
目的 何を判断するためのテストか 本番リリースの可否、構成の決定
対象と範囲 測る機能・API、測らないもの 注文の流れの API 8本。管理画面は対象外
性能要件 目標スループット・応答時間・エラー率と、その計算の根拠 18 req/s、p95 300ms 以内(①の計算)
テストの種類 負荷・ストレス・耐久・スパイクのどれを行うか 負荷・ストレス・耐久(4時間)
シナリオ 業務の流れと、API ごとの比率・待ち時間 一覧 40%・詳細 30%・注文 10%…
環境 本番との違い(台数・スペック・データ量・外部連携) 本番の 1/2 構成、外部決済はモック
データ 件数・作り方・テスト後の戻し方 商品 10万件、会員 50万件
測るもの クライアント側とサーバー側の指標 p90/p95/p99、エラー率、CPU、メモリ、DB 接続数
合格基準 下の表 ―
体制・日程 実施者、立ち会い(DB・インフラ)、再テストの枠 2週間、うち再テスト 3日
中止の条件 環境を壊さないための止め方 エラー率 10% を超えたら止める

本番との違い(台数・データ量)は、必ず一覧にします。 半分の構成で測ったなら、結果をそのまま本番の性能とは言えないためです。テスト計画書の全体の項目は、関連記事「テスト計画書の書き方と項目一覧|開始基準・終了基準のテンプレート」で扱っています。

合格基準のテンプレート

合格基準は「指標・条件・目標値・測り方」をそろえて表にします。 「速いこと」「安定していること」のような書き方では、判定できません。

指標 条件 目標値 測り方
スループット 負荷テスト(目標負荷 30分) 18 req/s を処理できる k6 の http_reqs
応答時間(画面の API) 同上 p90 200ms 以内、p95 300ms 以内 k6 の http_req_duration
応答時間(注文確定) 同上 p95 1秒以内、p99 2秒以内 注文 API だけをタグで集計
エラー率 同上 1% 未満 k6 の http_req_failed
資源 同上 CPU 平均 70% 以下、メモリの増加なし サーバーの監視
余力 ストレス(目標の 1.5倍) エラー率 1% 未満(応答時間は記録のみ) 同上
長時間 耐久(普段の負荷 4時間) 最初と最後の30分で p95 の差が 20% 以内 30分ごとの p95
戻り スパイク(4倍・1分) 負荷が戻ってから 60秒以内に p95 が目標内 5秒ごとの p95

k6 では、合格基準を「閾値(thresholds)」としてスクリプトに書けます。 閾値を満たさないと、k6 は 0 以外の終了コードで終わるので、CI でも判定できます(手元では 99 でした)。

import http from 'k6/http';
import { check } from 'k6';

export const options = {
  scenarios: {
    steady: {
      executor: 'constant-arrival-rate',   // 1秒あたりの件数を決めて送る
      rate: 40, timeUnit: '1s', duration: '30s',
      preAllocatedVUs: 20, maxVUs: 100,
    },
  },
  thresholds: {
    http_req_failed: ['rate<0.01'],                       // エラー率 1% 未満
    http_req_duration: ['p(90)<200', 'p(95)<300'],        // p90・p95
  },
  summaryTrendStats: ['avg', 'min', 'med', 'p(90)', 'p(95)', 'p(99)', 'max'],
};

export default function () {
  const res = http.get('http://127.0.0.1:8060/');
  check(res, { 'status is 200': (r) => r.status === 200 });
}
  █ THRESHOLDS

    http_req_duration
    ✓ 'p(90)<200' p(90)=64.39ms
    ✓ 'p(95)<300' p(95)=186.23ms

    http_req_failed
    ✓ 'rate<0.01' rate=0.00%

constant-arrival-rate(到着率を決める実行方法)を使うと、「18 req/s」のような業務量からの目標を、そのまま負荷として書けます。 仮想ユーザーの数を決める constant-vus では、サーバーが遅くなると送る件数も減るので、目標の件数をかけられたかを別に確かめる必要があります。k6 のスクリプトの書き方とインストールは、関連記事「負荷テストの進め方とk6の使い方|スクリプト例と結果の読み方」で扱っています。

実験に使ったテスト用サーバー

// 同時に4件までしか処理できないサーバー(1件あたり約50ms+ときどき遅い)
const http = require('http');
const SLOTS = 4;
let busy = 0;
const queue = [];
function work(res) {
  busy++;
  const r = Math.random();
  const ms = r < 0.95 ? 40 + Math.random() * 20 : 150 + Math.random() * 100; // 5% は遅い
  setTimeout(() => {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ ok: true, ms: Math.round(ms) }));
    busy--;
    if (queue.length) work(queue.shift());
  }, ms);
}
http.createServer((req, res) => {
  if (busy < SLOTS) work(res); else queue.push(res);
}).listen(8060, () => console.log('listening on 8060'));

段階的に負荷を上げたときは、件数を環境変数で渡し、結果を JSON に書き出して表にしました。

for R in 20 40 60 70 75 80 90; do
  k6 run -q -e RATE=$R --summary-export=sum_$R.json step.js
done
k6 run -e RATE=40 --out csv=raw.csv step.js      # 1件ずつの応答時間を CSV に

④ 結果の判定と報告

報告では「合格か」に加えて、「どこが限界で、何がボトルネックか」を書きます。 次の構成で書くと、読む人が判断しやすくなります。

項目 書くこと 例
結論 合格基準ごとの判定 8項目中 7項目合格、耐久の p95 増加が 28%(基準 20%)
結果の表 指標・目標値・測った値・判定 上の合格基準の表に「結果」「判定」の列を足す
限界 スループットが頭打ちになった点 約70件/秒(目標 18件/秒の約3.9倍)
ボトルネック 何が先に限界になったか、その根拠 DB の接続プール(上限 4 で待ちが発生)
本番との違い 環境の違いと、結果への影響の見込み 本番は 2倍の台数、データは 1/10
残った課題 基準を満たさなかったもの、対応と期限 耐久のメモリ増加を調査、次回リリース前に再測定

グラフは「時間 × p95」と「負荷 × スループット」の2枚があると、限界と戻りが一目で分かります。上のスパイクの表を時間の折れ線にすると、急増からの戻りが見えます。

不合格のときは、どれだけ足りないか(例:目標 300ms に対して 450ms)と、ボトルネックの根拠(CPU・DB・接続プールのどれが先に限界か)を書きます。サーバーを増やして解決するかの判断は、関連記事「スケールアップとスケールアウトの違い|負荷分散方式の一覧と選び方」で扱っています。

よくある質問

Q. 性能要件が要件定義書に書かれていません。 A. ①の表の数字(1日の件数・ピークの集中・1件あたりの API 回数)を、業務の担当者と今のシステムのログから集めて、目標の案を作ります。根拠の数字と一緒に合意しておくと、あとで目標が変わったときに計算し直せます。非機能要件の全体は、関連記事「非機能要件一覧|IPA非機能要求グレード6大項目と定義書テンプレート」で扱っています。

Q. p90 と p95 のどちらを使えばよいですか? A. 画面の応答は p90 か p95、決済など遅れると影響の大きい処理は p99 まで決めることが多いです。測る件数が少ないと p99 は数件の値で決まるので、p99 を使うなら、少なくとも数千件は集めます(1,000件なら p99 は遅い方から10件目あたりの値です)。

Q. テスト環境が本番より小さいです。 A. 本番との違いを計画書と報告書に書き、限界(頭打ちの点)とボトルネックを中心に報告します。「本番の 1/2 構成で目標の 3.9倍まで処理できた」のように、余力として示すと判断に使えます。

Q. 負荷をかけるマシンが先に限界になることはありますか? A. あります。負荷をかける側の CPU が高いと、応答時間が実際より長く測られます。この記事の測定は、サーバーと k6 を同じマシンで動かしていて、上限が約70件/秒と小さいので影響は小さいですが、大きな負荷をかけるときは、負荷をかける側の CPU も記録します。

まとめ

  • 性能要件は 業務量(件数・ピークの集中・API 回数・偏り・伸び)から計算する。例では 12,000件/日 → 18 req/s
  • 「同時ユーザー」と「同時に処理中の要求」は別物。リトルの法則(L = λW)で計算する
  • 応答時間の目標は p90・p95(重要な処理は p99)。実測では平均 62ms、p95 186ms と大きく違った
  • p95 は遅い要求の割合が 5% 前後だと測るたびに変わる。複数回・長めに測る
  • ストレス・限界テストでは、スループットの頭打ち(実測で約70件/秒)と p95 の急増を見る。エラー 0% でも p95 は 3秒を超えた
  • スパイクは 「何秒で戻ったか」 を合格基準に入れる(実測では約20秒)
  • 合格基準は 指標・条件・目標値・測り方 の表にし、k6 なら thresholds で自動判定する

参考・出典

確認日はいずれも 2026年10月13日です。

  • Grafana k6 ドキュメント「Thresholds」:https://grafana.com/docs/k6/latest/using-k6/thresholds/
  • Grafana k6 ドキュメント「Executors」:https://grafana.com/docs/k6/latest/using-k6/scenarios/executors/
  • Grafana k6 ドキュメント「Dropped iterations」:https://grafana.com/docs/k6/latest/using-k6/scenarios/concepts/dropped-iterations/
  • Grafana k6 ドキュメント「Load test types」:https://grafana.com/docs/k6/latest/testing-guides/test-types/
  • k6 リリース(v2.3.0):https://github.com/grafana/k6/releases

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「性能テストの進め方|性能要件の決め方と合格基準のテンプレート」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?