性能要件定義書とは
性能要件定義書(Performance Requirements Document)は、システムが満たすべき速度・処理量・容量を数値目標として定義した設計書です。インフラ設計(サーバースペック・ネットワーク帯域)と性能テスト(負荷テスト・ストレステスト)の合否基準として使用されます。
💡 性能要件を数値化する重要性
「速いこと」「重くならないこと」といった曖昧な要件は、インフラ設計の根拠にも性能テストの合否判定にもなりません。性能要件はレスポンスタイム・スループット・同時接続数・データ量など、具体的な数値で定義することで初めて設計・テストの基準として機能します。
① レスポンスタイム要件
画面・APIごとのレスポンスタイム(応答時間)目標値を定義します。「平均」だけでなく「ピーク時(95パーセンタイル)」の目標値も設定します。
| 対象 | 測定条件 | 平均レスポンスタイム | 95%tile | 最大(許容上限) |
|---|---|---|---|---|
| 画面表示(参照系) | 通常負荷時(30同時接続) | 1秒以内 | 3秒以内 | 5秒以内 |
| 画面表示(検索・フィルタ) | 通常負荷時(30同時接続) | 2秒以内 | 5秒以内 | 10秒以内 |
| データ登録・更新 | 通常負荷時(30同時接続) | 3秒以内 | 5秒以内 | 10秒以内 |
| API(参照系) | 通常負荷時(100 req/s) | 200ms以内 | 500ms以内 | 1秒以内 |
| API(登録・更新系) | 通常負荷時(50 req/s) | 500ms以内 | 1秒以内 | 3秒以内 |
| 帳票PDF出力 | 単一実行 | 5秒以内 | 10秒以内 | 30秒以内 |
| CSVエクスポート(1万件) | 単一実行 | 10秒以内 | 20秒以内 | 60秒以内 |
⚠️ 「3秒以内」はユーザビリティの基準値
一般的に「Webページは3秒以内に表示されないとユーザーが離脱する」と言われています。重い処理(帳票出力・大量CSV)は3秒を超えても許容されますが、画面遷移やAPI応答は可能な限り3秒以内を目指します。Google の Core Web Vitals(LCP: Largest Contentful Paint)も2.5秒を良好、4秒以上を不良としています。
② スループット・同時接続数要件
システムが処理できる同時ユーザー数・秒間リクエスト数(TPS/RPS)を定義します。ピーク時のアクセス集中を見越した設計値を設定します。
| 指標 | 通常時 | ピーク時(通常の3倍想定) | 根拠 |
|---|---|---|---|
| 同時接続ユーザー数 | 30ユーザー | 100ユーザー | 全ユーザー数200名の50%が業務ピーク時間帯に利用 |
| 秒間リクエスト数(画面) | 50 req/s | 150 req/s | — |
| 秒間リクエスト数(API) | 100 req/s | 300 req/s | — |
| 1日あたりリクエスト数 | 約50万リクエスト/日 | — | 9時〜18時の業務時間帯に集中 |
③ 容量設計
ストレージ容量・ネットワーク帯域・DBサイズの設計値を定義します。データ項目定義書のデータ量見積と連携します。
| 項目 | 初期容量 | 5年後推定容量 | 対策 |
|---|---|---|---|
| DBデータサイズ | 2GB(移行分含む) | 約15GB | 3年でアーカイブ処理。DB容量は50GBを確保 |
| DBログ・バックアップ | 5GB | 50GB | 週次バックアップ(12ヶ月保持) |
| アプリログ | 1GB/月 | 累計60GB | 1年経過後にS3/ファイルサーバーに退避 |
| 帳票PDFストレージ | 0GB | 約3GB/年 | S3管理(ライフサイクルポリシーで5年後削除) |
| ネットワーク帯域(上り) | 10Mbps | 100Mbps | クラウド環境でオートスケール対応 |
④ 可用性・RTO/RPO
システムの稼働率目標と、障害発生時の復旧目標時間(RTO)・復旧目標時点(RPO)を定義します。
| 指標 | 目標値 | 根拠・備考 |
|---|---|---|
| 稼働率(SLA) | 99.9%以上(月間) | 月間43.8分以内のダウンタイムを許容 |
| 計画停止時間 | 毎月第2日曜02:00〜04:00(2時間) | 月次メンテナンス。ユーザーへ事前通知必須 |
| RTO(目標復旧時間) | 4時間以内 | 業務開始前(9:00)までに復旧完了が目標 |
| RPO(目標復旧時点) | 1時間以内 | DBバックアップ頻度:1時間ごとのWALアーカイブ |
| バックアップ頻度 | フルバックアップ:週1回 / 差分:日次 | WALアーカイブによるPoint-in-Time Recovery(PITR)対応 |
⑤ スケーラビリティ方針
ユーザー増加・データ増加に備えたスケーラビリティ方針を定義します。
- 水平スケーリング(Webサーバー):APサーバーは最大10台までオートスケール。ロードバランサーでラウンドロビン分散
- 垂直スケーリング(DBサーバー):DBはRead Replicaを追加して参照クエリをオフロード。5年でスケールアップを検討
- キャッシュ戦略:マスタデータ(商品カテゴリ等)はRedisにキャッシュ(TTL: 5分)。DB負荷を軽減
- CDN活用:静的ファイル(CSS/JS/画像)はCDN経由で配信。APサーバー負荷を低減
Python Tips — Locustで負荷テストシナリオを作る
"""
Locustを使った負荷テストシナリオ。性能要件の数値を合否基準として検証する。
pip install locust
実行: locust -f locustfile.py --host=http://localhost:8000
--users 30 --spawn-rate 3 --run-time 5m --headless
"""
from locust import HttpUser, task, between, events
import json
import time
class AppUser(HttpUser):
"""通常ユーザーの操作シナリオ"""
wait_time = between(1, 3) # 操作間隔: 1〜3秒
def on_start(self):
"""セッション開始: ログイン"""
resp = self.client.post("/api/v1/auth/login/", json={
"email": "testuser@example.com",
"password": "testpass123"
})
self.token = resp.json().get("access_token", "")
@property
def auth_headers(self):
return {"Authorization": f"Bearer {self.token}"}
@task(5) # 重み: 5(最も頻繁)
def view_product_list(self):
"""商品一覧表示(参照系 - 最頻アクセス)"""
with self.client.get(
"/api/v1/products/",
headers=self.auth_headers,
name="/api/v1/products/ [一覧]",
catch_response=True
) as resp:
if resp.elapsed.total_seconds() > 3.0:
# 性能要件: 3秒以内
resp.failure(f"レスポンスタイム超過: {resp.elapsed.total_seconds():.2f}s")
elif resp.status_code != 200:
resp.failure(f"HTTP {resp.status_code}")
@task(2)
def view_product_detail(self):
"""商品詳細表示"""
product_id = 1 # テストデータのID
with self.client.get(
f"/api/v1/products/{product_id}/",
headers=self.auth_headers,
name="/api/v1/products/{id}/ [詳細]",
catch_response=True
) as resp:
if resp.elapsed.total_seconds() > 3.0:
resp.failure(f"レスポンスタイム超過: {resp.elapsed.total_seconds():.2f}s")
@task(1)
def create_product(self):
"""商品登録(更新系 - 低頻度)"""
payload = {
"product_code": f"LOAD-{int(time.time() * 1000) % 100000:05d}",
"product_name": "負荷テスト商品",
"category_id": 1,
"unit_price": 1000,
"tax_type": 10,
}
with self.client.post(
"/api/v1/products/",
json=payload,
headers=self.auth_headers,
name="/api/v1/products/ [登録]",
catch_response=True
) as resp:
if resp.elapsed.total_seconds() > 5.0:
# 性能要件: 5秒以内(更新系)
resp.failure(f"レスポンスタイム超過: {resp.elapsed.total_seconds():.2f}s")
@events.quitting.add_listener
def on_quitting(environment, **kwargs):
"""テスト終了時に性能要件に対する合否を判定"""
stats = environment.runner.stats.total
print(f"\n=== 性能テスト結果 ===")
print(f"総リクエスト数: {stats.num_requests}")
print(f"失敗率: {stats.fail_ratio:.1%}")
print(f"平均レスポンスタイム: {stats.avg_response_time:.0f}ms")
print(f"95%tile レスポンスタイム: {stats.get_response_time_percentile(0.95):.0f}ms")
# 合否判定
if stats.avg_response_time > 1000: # 平均1秒以内
print("❌ 性能要件NG: 平均レスポンスタイムが1秒を超えています")
environment.process_exit_code = 1
elif stats.get_response_time_percentile(0.95) > 3000: # 95%tile 3秒以内
print("❌ 性能要件NG: 95%tileレスポンスタイムが3秒を超えています")
environment.process_exit_code = 1
else:
print("✅ 性能要件OK")
定義チェックリスト
| チェック項目 | 確認ポイント |
|---|---|
| □ レスポンスタイム目標値が数値で定義されているか | 画面種別・API種別ごとに平均・95%tile・最大値が定義されているか |
| □ 同時接続数・スループットが定義されているか | 通常時・ピーク時のユーザー数・TPS/RPSが定義されているか |
| □ 容量設計が完了しているか | 5年後推定のDBサイズ・ストレージ・ネットワーク帯域が定義されているか |
| □ 可用性目標(SLA)が定義されているか | 稼働率・計画停止時間・RTO/RPOが数値で定義されているか |
| □ スケーラビリティ方針が定義されているか | 水平/垂直スケーリング・キャッシュ・CDN活用方針が定義されているか |
| □ 性能テスト計画に連動しているか | 性能要件の数値が性能テストの合否基準として使用されることを確認 |