性能テストの進め方|負荷モデル・テストデータ・評価方法を実務で解説

システム開発

はじめに

システム開発では、要件定義でレスポンスタイムや処理件数などの性能要件を決め、サイジングによって必要なサーバーやクラウドリソースを見積もります。

しかし、サイジングで決めた構成が、そのまま本番環境で性能要件を満たすことを保証するわけではありません。

サイジングは、必要な処理能力からシステム構成やリソース量を見積もる工程です。そこで決めた構成に実際のアプリケーションやデータを組み合わせ、オンラインアクセスやバッチ処理が重なる実運用相当の条件でも性能要件を満たせるか確認するのが性能テストです。

サイジングの考え方については、以下の記事で詳しく解説しています。

オンライン処理のサーバーサイジング|負荷試験から必要台数を決める実務手順
オンオンライン処理のサイジングを実務ベースで解説。性能要件、同時稼働VUser、負荷モデル、TPS・p95、性能曲線、ボトルネック分析から、必要なCPU・メモリ・サーバー台数を決定するまでの考え方と手順を紹介します。

性能テストで難しいのは、大量のリクエストを発生させることではありません。何を再現し、得られた結果をどう評価するかによって、結論そのものが変わります。

実際、私が経験したプロジェクトでは、想定以上に短いレスポンスタイムが測定されたにもかかわらず、調査すると予定した処理の一部が最後まで実行されていなかったことがありました。

性能が「悪い」ときだけでなく、想定以上に「良い」ときにも、その結果を信用してよいのか確認する必要があります。

この記事は、これから性能テストを計画する方や、性能テストを実施したにもかかわらず本番で性能問題が発生した経験のある方に向けて、性能テストを成立させるための実務上のポイントを整理します。

  1. 性能テストで確認するのは「速さ」だけではない
    1. 性能テスト計画で決めておくこと
  2. 性能目標と合否判定を先に決める
    1. レスポンスタイムだけを性能目標にしない
    2. 平均レスポンスタイムだけでは遅い処理を見落とす
    3. 負荷条件によって合否基準を分ける
    4. 性能目標は解釈の余地がない形まで具体化する
  3. 実運用を負荷モデルに落とし込む
    1. 「利用者1,000人」を負荷条件へ変換する
    2. オンライン処理とバッチの競合を再現する
    3. 性能ピークは一日の最大アクセスとは限らない
    4. 処理量だけでなく性能リスクからシナリオを選ぶ
  4. 性能テストはテストデータで結果が変わる
    1. データ件数だけ本番相当にしても不十分
    2. 値の分布・選択性・ヒット件数を本番に近づける
    3. DB以外のデータ条件も再現する
    4. 格納データだけでなく入力値も設計する
    5. 将来のデータ量を想定する
    6. 本番データを使えない場合にどう作るか
    7. データの中身が同じでも物理状態は同じではない
  5. 本番との差分を前提にテスト環境を設計する
    1. 本番より小さい環境の結果を単純換算しない
    2. 性能特性が異なるクラウドリソースを安易に使わない
    3. スケールの追随時間も性能特性として確認する
    4. クラウド環境ではサービスごとの性能特性も確認する
    5. 外部システムの応答特性もテスト環境の一部
  6. 何を測定し、どう性能テストを実施するか
    1. 利用者側とシステム内部の両方を測定する
    2. テスト前に測定できる状態を作っておく
    3. AP・基盤・DB・業務担当の役割を決める
    4. 低負荷から段階的に負荷を上げる
    5. 初期条件をそろえ、再テストの回転数を見積もる
    6. 負荷生成側も監視する
  7. 「速かったからOK」にしてはいけない
    1. 想定よりもレスポンスが速かった
    2. DBの更新件数を確認すると処理が足りなかった
    3. 接続元側のポート不足で後続処理へ到達できなかった
    4. エラーの有無ではなく「処理の完全性」を確認する
  8. ボトルネックを特定して改善する
    1. 遅く見える場所と原因の場所は一致しない
    2. 処理経路に沿ってボトルネックを切り分ける
    3. 同じ時刻の測定結果を突き合わせる
    4. パラメータ変更ありきで考えない
    5. DBではSQL・統計情報・実行計画を確認する
    6. 一つ改善すると次のボトルネックが現れる
  9. 性能テストの結果を本番移行の判断につなげる
    1. 性能目標を満たしたことを結果で確認する
    2. 確認できなかった条件を洗い出して共有する
    3. 本番で確認する条件と監視体制を決める
  10. 性能テストのチェックリスト
  11. まとめ|性能テストは「結果を信用できるか」まで確認する

性能テストで確認するのは「速さ」だけではない

性能テストでは、レスポンスタイムを測定するだけでなく、実運用を想定した負荷を与えた状態で、システム全体が性能要件を満たせることを確認します。

テストを実施する前に、どのような負荷を再現し、どのようなデータと環境を使い、何をもって合格とするのかを決めておく必要があります。

性能テストの全体的な流れは次のようになります。

性能目標、負荷モデル、テストデータ、テスト環境、測定・実施、評価、改善、再テスト、本番移行判断までの性能テスト全体フロー

図1 性能テスト工程の全体フロー

※テスト環境の調達や構築はリードタイムを考慮して前倒しで進めます。図は性能テストの設計・実施・改善サイクルと、その結果を本番移行判断へつなげる関係を示しています。

この記事で特に重視するのは、次の3点です。

  • いつ、どれだけ処理するのかを負荷モデルへ落とし込む
  • 1回の処理で何をどれだけ読み書きするのかをテストデータで再現する
  • 得られた結果が本当に正しいのかを処理結果まで含めて確認する

この3つがずれると、負荷生成ツールで大量のリクエストを送っても、本番性能を確認したことにはなりません。

性能テスト計画で決めておくこと

性能テストを始める前に、少なくとも次の項目を整理します。

項目決めておくこと
性能目標レスポンスタイム、処理量、バッチ時間など
負荷モデルTPS・RPS、同時実行、業務構成、負荷の時間変化
テストデータデータ量、値の分布、ヒット件数、入力値
テスト環境本番との差分と測定結果への影響
測定・実施取得するログやメトリクス、負荷のかけ方
体制AP、基盤、DB、業務担当などの役割
合否判定どの測定値をもって合格とするか
再テスト問題発生時の修正・再実施に必要な期間

性能テストは一度実行して終わる工程ではありません。問題が見つかれば原因を分析し、改善したうえで同じ条件から再テストします。

以降では、この流れに沿って性能テストをどのように設計し、評価するのかを具体的に見ていきます。

性能目標と合否判定を先に決める

性能テストを実施する前に、何を満たせば合格なのかを決めておきます。

テスト結果を見てから「この程度なら問題ない」と基準を調整すると、性能テストが要件を検証する工程ではなく、得られた結果を追認する工程になってしまいます。

レスポンスタイムだけを性能目標にしない

オンライン処理ではレスポンスタイムが代表的な性能指標ですが、それだけでシステム全体の性能を評価できるとは限りません。

例えば、レスポンスタイムが目標以内でも、必要な処理量を確保できなければ性能要件を満たしたことにはなりません。バッチ処理では終了時間、オンライン処理では処理量やエラー率など、要件に応じて複数の指標を組み合わせます。

平均レスポンスタイムだけでは遅い処理を見落とす

平均値だけを見ると、一部の利用者が経験する大きな遅延が隠れることがあります。

そこで、必要に応じてp90、p95、p99などのパーセンタイルを使用します。例えばp95が2秒であれば、測定した処理の95%が2秒以内に完了したことを表します。

どのパーセンタイルを合否判定に使うかは、サービス特性や性能要件によって決めます。p99を使えば常に適切というわけではなく、測定件数が少なければ末尾の値は少数の処理に大きく左右されます。

どの時間帯を集計対象とするのかも事前に決めます。Ramp-upやウォームアップを含めるのか、定常状態だけを評価するのかで結果は変わります。区間ごとに算出したp95を単純に平均して、全体のp95とすることもできません。

負荷条件によって合否基準を分ける

通常時、ピーク時、長時間継続時、性能限界を確認する条件では、テストの目的が異なります。

例えば通常時には利用者向けのレスポンスを重視し、ピーク時には必要な処理量を維持できるか、長時間のテストでは時間の経過とともに性能が劣化しないかを確認します。

性能限界を確認するテストでは、負荷を段階的に増やし、どこから処理量が伸びなくなるのか、エラーや急激なレスポンス悪化が発生するのかを確認します。

すべての負荷条件を同じ基準で評価するのではなく、そのテストで何を確認するのかに応じて合否基準を設定します。

性能目標は解釈の余地がない形まで具体化する

性能目標には、対象となる処理、負荷条件、測定指標、目標値、集計方法を含めます。ログイン、検索、更新などで性能特性や重要度が異なる場合は、処理ごとに目標を設定します。

例えば「平均レスポンスタイム3秒以内」だけではなく、

「ピーク時の対象処理について、定常測定区間のp95が3秒以内」

というところまで具体化します。

こうしておけば、テスト後に基準の解釈を変える余地がなくなり、性能テストの結果をそのまま合否判定や本番移行の判断に使えます。

実運用を負荷モデルに落とし込む

性能テストでは、本番で発生する業務負荷を、テスト環境で再現できる条件に置き換えます。そのために作成するのが「負荷モデル」です。

利用者数だけでは負荷の大きさは決まりません。同時実行数、RPS、TPS、業務構成、操作間隔、負荷の上がり方、継続時間などに分解し、実運用を数値として表します。

「利用者1,000人」を負荷条件へ変換する

非機能要件に「最大利用者数1,000人」とあったとします。そのうち900人が朝9:00〜10:00の1時間にシステムを利用するケースを考えます。

900人がこの1時間に1回ずつ処理すると、3,600秒で900トランザクションなので平均0.25 TPSです。さらに、開始後10分間に半数の450件が集中するなら、その区間は600秒で450トランザクション、平均0.75 TPSになります。

1業務が平均5回のHTTPリクエストで構成されるなら、単純化した例では0.75 TPS×5で約3.75 RPSです。

定常状態を前提とすれば、リトルの法則から、平均して処理中にある要求数は「TPS×平均応答時間」で捉えられます。0.75 TPS、平均応答時間2秒なら、平均して処理中にあるトランザクションは約1.5件です。

ただし、これはあくまで定常状態の平均値です。アクセスが短時間に偏れば瞬間的な負荷は高くなります。同じ0.75 TPSでも平均応答時間が2秒から10秒へ悪化すれば、平均して処理中にあるトランザクションは1.5件から7.5件へ増えます。

利用者900人という数字と、ある瞬間にシステム内部で処理されている要求数は別のものとして考える必要があります。

観点確認する内容の例
同時実行同じ時間帯にどの程度の処理が重なるか
RPS/TPS1秒間のリクエスト数/業務トランザクション数
業務構成検索、登録、更新などの実行比率
操作間隔次の操作までの時間
負荷の上昇ピークへ到達するまでの時間
継続時間ピーク状態が続く時間

重要なのは、負荷生成ツールへ設定しやすい数字を先に決めるのではなく、実際の業務量から負荷条件を導き出すことです。

オンライン処理とバッチの競合を再現する

例えば、9:00〜10:00にオンライン利用が集中し、9:10から10分間、複数のバッチも同時に実行されるとします。

オンラインとバッチが同時にCPU、メモリ、ストレージI/O、DB接続などを使用すれば、単独実行では現れなかった競合が発生する可能性があります。

性能テストで再現したいのは、単純な最大利用者数ではありません。性能上もっとも厳しくなる時間帯に、システム全体で何が同時に動いているのかです。

性能ピークは一日の最大アクセスとは限らない

月末、四半期末、年度末にデータ量や処理件数が増えるシステムもあります。年に一度しか実行されない処理が最大の性能リスクになる場合もあります。

現行システムがある場合はアクセスログや処理実績、新規システムでは業務カレンダーや年間の業務スケジュールなどから、日次、月次、年次、特定イベント時に発生するピークを洗い出します。

処理量だけでなく性能リスクからシナリオを選ぶ

すべての機能を本番と同じ比率で再現する必要はありません。テスト期間や環境には制約があるため、処理量と性能リスクの両面から対象を選びます。

大量データを処理する機能、排他競合が発生しやすい機能、新しい処理方式を採用した機能などは、利用頻度が低くても性能リスクが高いと考えられます。

  • 処理量:多/リスク:高 → 必ず含める
    例:ピーク時に集中する主要更新処理
  • 処理量:多/リスク:低 → 構成比に応じて含める
    例:ログイン、一般的な参照・照会処理
  • 処理量:少/リスク:高 → 個別に測定する
    例:年次バッチ、大量データの一括更新
  • 処理量:少/リスク:低 → 今回のテスト範囲から外す
    例:利用頻度が低く処理方式も単純な参照処理

性能リスクを判断できない新機能や新方式は、低リスクとみなさず高リスク側に置きます。

こうして業務負荷をモデル化できたら、次に考えるのが、その負荷をどのようなデータに対して発生させるかです。

同じ件数のリクエストでも、データ量や値の分布、ヒット件数が異なれば、得られる性能結果も変わります。

性能テストはテストデータで結果が変わる

負荷モデルを本番相当にしても、テストデータが実運用とかけ離れていれば、性能テストの結果は信用できません。

特にデータベースを利用するシステムでは、データ件数だけでなく、値の分布、検索・更新の対象件数、1件あたりのデータサイズなどによって処理量が変わります。

性能テストでは「本番と同じ件数のデータを入れる」ことではなく、性能に影響するデータの特性を本番に近づけることが重要です。

データ件数だけ本番相当にしても不十分

例えば、本番とテスト環境の両方に100万件のデータが登録されていたとします。

本番では検索条件に該当するデータが1,000件しかないのに、テスト環境では50万件が該当していれば、同じSQLでも読み取りや絞り込みに必要な処理量は大きく異なります。

逆に、本番では大量のデータを処理するSQLが、テスト環境では数件しかヒットしなければ、実際より短いレスポンスタイムが測定されます。

データ件数だけでなく、1回の処理で実際にどれだけのデータを読み、絞り込み、更新するのかまで再現する必要があります。

値の分布・選択性・ヒット件数を本番に近づける

特に注意したいのが、WHERE句やJOIN条件、GROUP BYなどで使用されるカラムです。

値の種類や偏りが変われば、「検索条件によって全体の何%まで絞り込まれるか」という選択性も変わります。

データベースが統計情報をもとに実行計画を決定する場合、値の分布や選択性が本番と異なることで、テスト環境では本番とは異なるアクセス方法が選ばれる可能性があります。

性能テスト用データでは、総件数とあわせて、主要な検索条件の値の分布とヒット件数を確認します。

DB以外のデータ条件も再現する

性能に影響するデータは、データベース内のレコードだけではありません。

対象確認する条件の例
レコード1件あたりのデータサイズ、文字列長、LOBサイズ
ファイルファイルサイズ、ファイル数、1回に処理する総データ量
キャッシュキャッシュ対象となるデータ量、ヒットしやすさ
キュー一度に滞留するメッセージ件数、1件あたりのサイズ
外部連携1回に受け渡す件数やデータ量

同じ処理件数でも、1件あたりのデータ量が異なれば、ストレージI/O、メモリ使用量、ネットワーク転送量などは変わります。

格納データだけでなく入力値も設計する

入力値は負荷生成スクリプトに設定しますが、性能を左右するのはその値の分布です。入力値もテストデータ設計の一部として考えます。

例えば、同じユーザーIDや同じ更新キーばかりを使用すると、本番では発生しないロック競合や偏ったキャッシュヒットを作る可能性があります。

逆に、性能リスクが高いとして選んだ排他競合の起きやすい機能でも、更新対象を本番以上に分散させれば、実運用で発生する競合を再現できません。

ユーザーID、検索条件、更新対象キーなども、実際の利用状況に近い分布で投入します。

将来のデータ量を想定する

サービス開始直後のデータだけで性能を確認しても、数年後にデータ量が増えた状態で同じ性能が出るとは限りません。

非機能要件で将来のデータ量が定義されている場合は、システムライフサイクルの中で性能上もっとも厳しくなる時点を想定してテストデータ量を決めます。

本番データを使えない場合にどう作るか

個人情報などの理由から、本番データをそのまま性能テスト環境へ持ち込めない場合があります。その場合は、マスキングしたデータや疑似データを作成します。

注意したいのは、情報を置き換えることだけを目的に値をランダム化し、本番に存在する値の偏りやデータ間の関係まで失わないことです。

例えば、本番では長さが大きく異なる文字列を固定長のダミー文字列へ一括置換すると、データサイズやI/O量まで本番と変わる可能性があります。マスキング後も、性能に影響するデータ特性が維持されているかを確認します。

データの中身が同じでも物理状態は同じではない

一括ロードした直後のテスト環境と、長期間更新を繰り返してきた本番環境では、テーブルや索引の物理的な状態、統計情報などが異なる場合があります。

同じ件数、同じ値の分布を持つデータでも、I/O特性やSQLの実行計画が本番と一致しないことがあります。

性能テストでは、データの論理的な内容を合わせるだけでなく、本番とテスト環境のデータ状態に性能へ影響する差がないかも確認します。

Oracleでは、統計情報の状態が変わることでカーディナリティ推定や実行計画が変化し、SQL性能へ影響することがあります。実際にOracle 19cへの更改後、パーティション操作を契機に統計情報と実行計画が変化した事例は、以下の記事で詳しく解説しています。

Oracle 19c更改後に実行計画が変わる――パーティション削除時の統計情報更新と影響調査漏れ
Oracle Database 19cへの更改後、DROP PARTITIONを契機に表統計の一部が更新され、実行計画とSQL性能が変化した事例を解説。AWRやPLAN_HASH_VALUEによる原因調査、統計情報の再収集、SQL Plan Baseline、更改時の影響調査と試験の観点を整理します。

こうして負荷モデルとテストデータを決めたら、次は、それらをどの環境で再現するかを考えます。

本番との差分を前提にテスト環境を設計する

性能テストでは本番と同じ構成の環境が理想ですが、コストや準備上の制約から、本番より小さい環境で実施することもあります。

重要なのは、本番と何が違い、その差が測定結果にどのような影響を与えるのかを説明できることです。

本番より小さい環境の結果を単純換算しない

「CPUが本番の半分だから処理能力も半分」「APサーバーが2台ではなく1台だからTPSも半分」といった単純換算が成立するとは限りません。

システム性能はCPU、メモリ、ストレージI/O、ネットワーク、DB接続など複数の要素で決まります。

例えば、CPUを半分にしても処理の大半がI/O待ちなら、レスポンスへの影響は小さいかもしれません。逆にメモリを減らしたことでDBやOSのキャッシュに載るデータが減れば、ストレージI/Oが大きく増え、レスポンスが急激に悪化することもあります。

環境差分はスペックの比率ではなく、その差によってどのリソースの使われ方が変わるのかを評価します。

性能特性が異なるクラウドリソースを安易に使わない

性能特性が本番と異なるインスタンスタイプを、安価という理由だけで性能テスト環境へ採用すると、測定結果をそのまま本番へ当てはめられません。

例えばAmazon EC2のT系では、CPUクレジットのモードによって挙動が変わります。Standardではクレジットが不足するとCPU使用率がベースラインへ低下します。Unlimitedではベースラインを超えたCPU利用を継続できますが、利用状況によって追加料金が発生します。

T3、T3a、T4gは原則Unlimited、T2はStandardがデフォルトですが、設定は変更できます。

本番自体がT系であれば、CPUクレジットの推移やUnlimited時の追加料金も含めて性能特性として評価します。

スケールの追随時間も性能特性として確認する

負荷モデルでは、ピークへ到達するまでにどの程度の時間をかけて負荷を上げるかを決めました。その負荷の上げ方と、環境側が処理能力を増やす速度が噛み合うかも確認します。

例えばAuto Scalingでは、負荷を検知してからインスタンスを起動し、アプリケーションが処理可能になるまで時間が必要です。

テスト開始直後から最大負荷を投入してエラーになった場合、それがシステムの定常的な処理能力の限界なのか、スケールアウトが追いつかなかったのかを分けて考える必要があります。

定常状態で処理できるTPSと、負荷急増時に必要な処理能力へ到達するまでの時間は、別の性能特性として評価します。

クラウド環境ではサービスごとの性能特性も確認する

確認項目性能テストで確認すること
T系EC2CPUクレジットのモード、残高、Unlimited時の追加料金
gp2I/Oクレジットにより、短時間では性能低下が表面化しない場合がある
gp3設定したIOPS・スループットが本番と同等か
EC2のEBS性能EBSに十分な性能を設定しても、EC2側の帯域・IOPSが先に上限へ達しないか
サービスクォータテスト環境と本番で上限値が異なっていないか
スロットリングテスト環境固有の上限か、本番でも同じ制御を受けるのか
Auto Scalingスケール開始条件、起動・初期化・ウォームアップ時間
ロードバランサー急激な負荷増への追随条件、必要に応じたキャパシティ確保
課金高負荷テスト、CPUクレジット、データ転送などによる費用
外部システム接続可否、代替環境の応答時間・データ量

外部システムの応答特性もテスト環境の一部

外部システムと連携する機能では、性能テストから相手の本番環境へ大量のリクエストを送れない場合があります。

スタブなどで代替するときも、正常応答を返すだけでは本番相当とはいえません。応答時間、受け渡すデータ量、同時実行時の挙動など、性能に影響する条件をどこまで再現するのかを決めておきます。

参考

ここまでで、負荷、テストデータ、それを実行する環境の条件がそろいました。次は、性能目標の判定と問題発生時の原因分析に必要な測定方法を決めます。

何を測定し、どう性能テストを実施するか

負荷モデル、テストデータ、テスト環境を決めたら、性能テストを実施できる状態を整えます。

利用者側とシステム内部の両方を測定する

レスポンスタイムや処理件数、エラー率などは、性能目標を満たしたかを判断するために測定します。

これとは別に、原因分析のため、CPU、メモリ、I/O、ネットワーク、アプリケーション、データベースなどの状態も取得します。

性能テストでは、合否を判定する測定値と、なぜその結果になったのかを説明する測定値の両方が必要です。具体的なボトルネックの切り分け方は後述します。

テスト前に測定できる状態を作っておく

性能問題が発生してからログやメトリクスを追加しても、そのテスト時点の状態をさかのぼって取得することはできません。

アプリケーションログ、アクセスログ、OS・ミドルウェアのメトリクス、DBの性能情報など、原因分析に必要な情報をテスト前に取得できる状態にしておきます。

各サーバーや監視基盤の時刻がずれていると、同じ時間帯に発生した事象を正しく突き合わせられません。性能テストでは測定対象の時刻同期も確認します。

OracleでAWRを使用する場合はDiagnostics Packのライセンス条件を事前に確認します。利用できない環境ではStatspackなど、別の測定手段を準備します。

AP・基盤・DB・業務担当の役割を決める

性能テストは、負荷生成ツールを操作する担当者だけでは成立しません。

担当主に確認するもの判断すること
業務・AP処理結果、アプリケーションログ業務処理が正しく完了したか
システム基盤CPU、メモリ、I/O、ネットワークインフラにボトルネックがないか
DBSQL、待機状態、DBリソースDB処理が性能低下の原因か
テスト実施負荷条件、投入件数、測定結果計画した負荷を再現できたか

誰がどの情報を確認し、問題発生時に誰が切り分けるのかを事前に決めておくことで、測定後の調査をすぐに始められます。

各担当の分析範囲だけでなく、性能テスト全体の合否を誰が判断するのかも事前に決めておきます。

低負荷から段階的に負荷を上げる

最初から最大負荷を投入するのではなく、まず低負荷でテストシナリオ、入力データ、処理結果、監視項目が正しく動作することを確認します。

その後、負荷モデルで決めた条件に従って段階的に負荷を上げます。

負荷を段階的に上げれば、どの負荷水準から性能が変化したのかを把握しやすくなります。スクリプトや測定方法の不備によるエラーを、最大負荷時の性能限界と誤認することも防げます。

初期条件をそろえ、再テストの回転数を見積もる

性能テストでは、キャッシュ状態やDBデータなどの初期条件によって結果が変わります。JavaのJITコンパイルやDB・OSのキャッシュなど、起動直後と一定時間処理した後で性能特性が変わる仕組みもあります。

比較するテストでは、どの条件から開始したのかを記録し、可能な範囲で同じ状態を再現します。

更新系テストでは、実行するたびにDBの状態が変わります。同じ条件で再テストするには、バックアップやスナップショットなどからデータを戻す作業が必要になる場合があります。

本番相当のテストデータを最初に作成する時間に加え、再テストのたびに必要となるリストア時間も計画へ含めます。1回のテストが1時間でも、初期化に数時間かかれば、1日に実施できる回数は限られます。

性能テストは問題発見、修正、再テストを繰り返すことを前提に、1回の実行時間ではなく、テストを何回転できるかでスケジュールを考えます。

負荷生成側も監視する

性能テストで限界に達するのは、テスト対象のシステムとは限りません。

負荷生成サーバーのCPUやメモリ、ネットワーク、ファイルディスクリプタ、接続元で利用できるポートなどが不足すると、予定した負荷そのものを発生できなくなります。

この状態でテスト対象の処理量が頭打ちになっても、システムの性能限界とは判断できません。

負荷生成ツールに設定した値だけではなく、実際に計画したRPSやTPSを発生できているかも確認します。

こうして測定と実施条件を整えたうえで性能テストを行います。次に、その測定結果自体を信用してよいのかを確認します。

「速かったからOK」にしてはいけない

性能テストでは、レスポンスタイムが目標値を満たしていれば合格、と判断したくなります。

しかし、性能が想定以上に良い場合にも注意が必要です。処理が速かったのではなく、本来実行されるはずの処理が途中までしか実行されていないため、結果だけが良く見えていることがあるからです。

代表的には、次のようなケースがあります。

良く見える原因実際に起きていること
HTTPレスポンスだけで成功判定HTTP 200でも業務処理はエラーになっている
検索結果がほとんどない本番相当の検索・集計処理が実行されていない
後続処理まで到達していないタイムアウトや接続失敗などで処理が途中終了している
キャッシュだけで応答している想定していたDBアクセスなどが発生していない

性能テストでは、「レスポンスが返った」ことと「期待した業務処理が完了した」ことを分けて確認する必要があります。

以下は、私が実際に経験した性能テストでの事例です。

想定よりもレスポンスが速かった

実際のプロジェクトで、負荷をかけた際のレスポンスタイムが想定よりもかなり短くなったことがありました。

性能目標は満たしており、数値だけを見れば良好な結果です。それでも、負荷を増やしているのに測定結果が想定したほど変化しない場合は注意が必要です。

本当に処理能力に余裕がある可能性もありますが、投入した負荷が後続処理まで到達していないため、CPUやDBなどに予定した負荷がかかっていない可能性もあります。

そこで、「性能が良かった」と結論づける前に、実際に予定した処理が実行されているかを確認しました。

DBの更新件数を確認すると処理が足りなかった

最初にアプリケーションのログを確認しましたが、性能低下を直接示すような目立ったエラーは見つかりませんでした。

正常処理を示すログの件数は、想定より少ないことが分かりました。

さらにデータベースの更新結果を確認すると、本来テストで実行した件数に対応するだけのデータが更新されていませんでした。

結果として、重い後続処理を実行していないリクエストが多数存在し、システム全体のレスポンスタイムが実態以上に良く見えていました。

接続元側のポート不足で後続処理へ到達できなかった

Webサーバー側の状況を調べたところ、このケースではWebサーバーからAPサーバーへの接続時に、接続元側で利用可能なTCPポートが不足し、コネクションを確立できない状態が発生していました。

Webサーバーが受け付けたリクエストの一部はAPサーバーへ正常に到達せず、本来実行されるはずだった後続処理が実行されていませんでした。

TCPポートが不足する要因には、短時間に大量の接続を生成する構成やコネクションの使い方など複数の可能性があります。このケースについては、接続元で利用可能なポートが不足していたところまでを確認しており、それ以上の原因を一般化することはできません。

エラーの有無ではなく「処理の完全性」を確認する

必要に応じて、処理の各段階で件数を突き合わせます。

負荷生成ツールから投入した件数
→ Webサーバーが受け付けた件数
→ アプリケーションが処理した件数
→ DBで更新された件数
→ 最終的な業務処理結果

例えば1万件の更新要求を送ったのであれば、レスポンスタイムが目標以内だったことだけでなく、本当に1万件分の業務処理が完了したのかを確認します。

件数が途中で減っているなら、その境界付近に問題が存在する可能性があります。

テストデータ設計もここに影響します。検索結果がほとんどないデータを使えば、本番で必要となる検索や集計処理が実行されず、処理自体は正常に終了していても性能が良く見える場合があります。

性能テストの合否は、単に「速かったか」では決められません。

要求した負荷をシステムへ与え、その処理が正しく最後まで完了したうえで、性能目標を満たしていることを確認する。

性能目標を満たさない場合だけでなく、想定した処理が完了していない場合にも、どこで処理が滞留・中断しているのかを切り分ける必要があります。

ボトルネックを特定して改善する

性能目標を満たしていない場合や、予定した処理が途中までしか実行されていない場合は、取得したログやメトリクスから原因を切り分けます。

ここで重要なのは、レスポンスが遅くなった場所と、性能低下を引き起こしている場所が同じとは限らないことです。

遅く見える場所と原因の場所は一致しない

例えば、Web画面のレスポンスが遅くても、WebサーバーのCPU不足が原因とは限りません。

APサーバーでDB接続を待っている、DBでストレージI/O待ちが発生している、外部システムからの応答を待っているなど、後続処理の遅延が手前のレスポンスに現れている場合があります。

性能問題は、利用者から見えている場所ではなく、処理時間を実際に消費している場所を探す必要があります。

処理経路に沿ってボトルネックを切り分ける

性能分析では、処理の流れに沿って各レイヤーの状態を確認します。

図2 性能影響経路と構成要素の全体像

各レイヤーで処理開始・終了時刻や処理時間を取得できる場合は、隣接するレイヤー間でどこから待ち時間が増えているかを確認します。

上流で観測した処理時間に対して下流の実処理時間が短ければ、その間に接続待ち、キュー待ちなどが発生している可能性があります。処理経路に沿って差が大きくなる境界を探すことで、調査対象を絞り込めます。

処理件数が途中から減っていた場合も同じです。件数が減少した境界と、その時点のログやメトリクスを突き合わせます。

同じ時刻の測定結果を突き合わせる

個々のメトリクスを別々に見るだけでは、原因と結果の関係を判断しにくくなります。

例えば10:03にレスポンスタイムが悪化したのであれば、同じ時刻のCPU、I/O、APのスレッド、DB接続、SQL実行時間などを確認します。

「いつ遅くなったか」と「その時システム内部で何が変化したか」を同じ時間軸で見ることが、ボトルネック分析の基本です。

性能テスト前に各サーバーや監視基盤の時刻同期を確認しておくのは、この突き合わせを正しく行うためです。

パラメータ変更ありきで考えない

性能問題が発生すると、スレッド数やコネクションプール、メモリサイズなどのパラメータをすぐに変更したくなります。

原因を特定せずに設定を変更すると、効果がないだけでなく、別のリソース競合を発生させることがあります。

まず待ちが発生している場所を特定し、その原因に応じて、設定変更、SQL改善、処理方式の見直し、スケールアップやスケールアウトなどを検討します。

DBではSQL・統計情報・実行計画を確認する

DB処理がボトルネックであれば、長時間実行されているSQLや待機状態を確認し、必要に応じて統計情報や実行計画まで調査します。

テストデータの量や値の分布が本番と異なれば実行計画が変わることもあるため、SQLだけでなく、設計したテストデータの条件まで戻って確認する必要があります。

一つ改善すると次のボトルネックが現れる

性能上限は、最初に見つかった一つのリソースだけで決まるとは限りません。

例えばDBのボトルネックを改善して処理量が増えると、次はAPサーバーやストレージが上限に達することがあります。

性能テストでは、

測定 → 原因分析 → 改善 → 再テスト

を繰り返し、システム全体として性能目標を満たせる状態まで改善します。

そのうえで、どの条件まで確認できたのか、テストでは確認できなかった条件が何なのかを整理し、本番移行の判断へつなげます。

性能テストの結果を本番移行の判断につなげる

ボトルネックを改善して再テストを繰り返したら、最後に性能テストの結果を整理します。

ここで確認するのは、性能目標を満たしたかどうかだけではありません。どの条件まで確認でき、何を確認できなかったのかを明確にし、本番移行の判断につなげます。

性能目標を満たしたことを結果で確認する

事前に決めたレスポンスタイム、処理量、バッチ処理時間、エラー率などの合否基準に対して、最終的な測定結果を確認します。

結果を見てから基準を解釈し直すのではなく、事前に定めた基準に対して判定します。

どの負荷モデル、テストデータ、環境条件で得られた測定値なのかも残します。

性能テストの結果は、測定値と、その測定値を得た前提条件をセットで評価する必要があります。

確認できなかった条件を洗い出して共有する

本番とまったく同じ条件を性能テストで再現できるとは限りません。

例えば、本番より小さい環境で実施した、外部システムをスタブで代替した、年次ピークの一部を再現できなかった、といった条件が残ることがあります。

テストで確認できず、本番で性能問題につながる可能性が残るものを、ここでは残存リスクとして整理します。

「テストできなかった」で終わらせず、本番でどのような性能リスクとして残るのかを明確にし、移行判断を行う関係者で共有します。

本番で確認する条件と監視体制を決める

性能テストで再現できなかった条件は、本番移行後に確認します。

特に、初回のピーク時間帯、月末処理、大量バッチ、年次処理など、実運用にならなければ確認できない条件については、事前に監視対象と判断基準を決めておきます。

問題が発生した場合に誰が判断し、どのログやメトリクスを確認し、必要ならどのような対応を取るのかまで準備しておけば、性能テストで残ったリスクを本番運用へ引き継げます。

性能テストは、テスト結果を報告して終わる工程ではありません。確認できたことと確認できなかったことを明確にし、本番移行後まで含めて性能を管理することが最後の工程です。

性能テストのチェックリスト

チェック項目確認
利用者数だけで負荷条件を決めていないか
オンライン処理とバッチなど、同時に動く処理を考慮したか
テストデータの件数だけでなく、値の分布やヒット件数まで再現したか
本番環境との差分と、その差が性能に与える影響を整理したか
クラウド固有の上限やスケーリング特性を確認したか
性能分析に必要なログやメトリクスを事前に取得できる状態にしたか
負荷生成側が予定したRPS/TPSを発生できているか
レスポンスタイムだけでなく、要求した処理が最後まで完了したか確認したか
修正・データ復元・再テストを含めた実施回数を計画したか
テストで確認できなかった条件を、本番移行後の監視へ引き継いだか

まとめ|性能テストは「結果を信用できるか」まで確認する

性能テストは、大量のリクエストを発生させてレスポンスタイムを測るだけの工程ではありません。

実運用から「いつ、どれだけ処理するのか」を負荷モデルへ落とし込み、その1回の処理が「何をどれだけ読み書きするのか」をテストデータで再現する必要があります。

さらに、本番との環境差分を把握したうえで必要なログやメトリクスを測定し、性能目標を満たしているかだけでなく、要求した処理が正しく最後まで実行されていることまで確認します。

性能が悪ければボトルネックを探しますが、想定以上に性能が良い場合も、負荷が予定どおりシステムへ到達しているかを疑う必要があります。

性能テストは「速さを測る工程」ではなく、その測定結果を本番性能の判断材料として信用できるところまで検証する工程です。


関連記事

性能テストは、テスト工程だけで完結するものではありません。要件定義で決めた性能要件をもとに負荷条件やテストデータを設計し、テストで確認できなかったリスクは本番移行や運用へ引き継ぐ必要があります。
性能要件の考え方から、設計とテストのつながり、クラウド移行時の性能リスクまで確認したい方は、以下の記事もあわせてご覧ください。

機能要件と非機能要件の違い|「機能が動く」と「本番で使える」は違う
性能や可用性、運用性などを含む非機能要件について、機能要件との違いから実務での考え方まで整理しています。

機能要件と非機能要件の違い|「機能が動く」と「本番で使える」は違う
「機能が動く」と「本番で使える」は違う。機能要件と非機能要件の違いを、購入申請システムの具体例と現場の失敗パターンから整理します。

Vモデルは古くない|設計とテストをつなぐ品質保証の考え方
要件や設計で決めた内容を、どのテスト工程で確認するのか。設計とテストを対応づけて品質を作り込むVモデルの考え方を解説しています。

Vモデルは古くない|設計とテストをつなぐ品質保証の考え方
Vモデルは古い開発手法の話ではなく、要件・設計で決めたことをテストで確認する品質保証の考え方です。バックアップ要件の具体例で各工程の対応関係を整理します。

クラウドリフトPoCの設計リスク|可用性・性能・ストレージ・バックアップ・監視
AWSへのクラウド移行で、構築後に性能問題を出さないために何をPoCで確認すべきか。性能・可用性・ストレージを中心に、設計段階で検証すべきリスクを具体例から解説しています。

【第2回】クラウドリフトPoCの設計リスク|可用性・性能・ストレージ・バックアップ・監視
クラウドリフトPoCで「動いた」だけでは、構築・試験・運用まで成立するとは限りません。可用性・性能・ストレージ・バックアップ・監視の5領域について、PoCで確認すべき設計リスクと、見落とした場合の後工程への影響を実務例から整理します。

コメント

タイトルとURLをコピーしました