- はじめに
- End-to-Endパフォーマンス分析とは
- パフォーマンス問題の基本パターン
- 最初にResponse Time・Throughput・Error Rateを確認する
- 影響範囲を確認する
- Response Timeを分解する
- Throughputと同時処理数の関係を確認する
- Queueから待ちの場所を探す
- CPUボトルネックを分析する
- Memoryボトルネックを分析する
- Storage I/Oボトルネックを分析する
- Networkボトルネックを分析する
- Web ServerとTomcatを分析する
- JVMを分析する
- Databaseを分析する
- 外部APIとBackendの遅延を分析する
- 仮想化基盤とContainerを分析する
- 時系列で相関を見る
- 正常時と比較する
- 変更点を確認する
- End-to-Endトラブルシューティングの進め方
- 仮説を立てて検証する
- パフォーマンス分析で避けたいこと
- Performance Engineeringのサイクル
- まとめ
- 参考文献
はじめに
Webシステムの性能問題は、1つのレイヤーだけを見ても原因を特定できないことがあります。利用者から見えるResponse Timeは、Network、Web Server、Application Server、JVM、Database、OS、仮想化基盤、Hardwareなど、複数のレイヤーの処理時間や待ち時間が積み重なった結果だからです。
たとえばResponse Timeが急に悪化したとき、CPU使用率が高ければCPU不足を疑いたくなります。しかし、DatabaseのI/O遅延によってApplication Threadが長時間待機し、その結果としてQueueが増えている可能性もあります。逆にCPU使用率が低くても、Thread、Connection Pool、Lock、外部APIなどの待ちによってシステム全体が遅くなることがあります。
重要なのは、最初から原因を決めつけるのではなく、Response Time、Throughput、Error Rateを起点に影響範囲を確認し、どこで時間を使っているのか、どこで待ちが発生しているのかを順番に絞り込むことです。
本記事では、ClientからNetwork、Web Server、Tomcat、Application、JVM、Database、Linux、仮想化基盤、Hardwareまでを横断し、Webシステムのボトルネックを特定するためのEnd-to-Endのパフォーマンス分析方法を解説します。
End-to-Endパフォーマンス分析とは
End-to-Endパフォーマンス分析では、利用者からBackendまでの処理経路全体を対象に、Response TimeやThroughputの変化と各レイヤーの状態を関連付けて確認します。

実際には、すべてのRequestがこの順番で同じ処理を通るわけではありません。Cache、非同期処理、複数の外部API、複数Databaseなど、システムによって処理経路は異なります。まず対象Requestが実際にどのComponentを通るのかを整理することが出発点です。
パフォーマンス問題の基本パターン
性能問題は、発生の仕方によって調査の入口が変わります。最初に現象を分類すると、調査範囲を絞りやすくなります。
| 現象 | 最初に確認すること |
|---|---|
| Response Timeが全体的に遅い | 共通経路、Backend、Resource、Queue |
| Throughputが伸びない | 飽和しているResource、同時実行数、Queue |
| 特定Requestだけ遅い | 処理経路、SQL、外部API、データ依存 |
| ピーク時だけ遅い | CPU、Thread、Connection、Queue、DB負荷 |
| 時間経過とともに遅くなる | Memory、GC、Connection、Resource Leak、データ量 |
| 突然遅くなった | 変更点、障害、Backend遅延、Network、Infrastructure |
| 一部Nodeだけ遅い | Node間の設定差、Resource差、負荷偏り |
最初にResponse Time・Throughput・Error Rateを確認する
性能問題が発生したら、最初にResponse Time、Throughput、Error Rateの時間推移を確認します。平均Response Timeだけでは一部の遅いRequestを見逃すため、p95、p99、Maxや分布も確認します。
| 指標 | 確認すること |
|---|---|
| Response Time | いつから遅くなったか、p95・p99も悪化しているか |
| Throughput | 処理量が増えたか、逆に低下していないか |
| Error Rate | TimeoutやHTTP Errorが増えていないか |
Throughputの増加を伴わずResponse Timeだけが悪化している場合は、単純なアクセス増加以外の要因も考えます。Databaseや外部APIの遅延、Storage I/O、Lock競合などによって、1件あたりの処理時間が長くなっている可能性があります。
影響範囲を確認する
次に、性能問題がどこまで広がっているかを確認します。
| 確認項目 | 例 |
|---|---|
| 機能 | 全画面か、特定APIだけか |
| 利用者 | 全Userか、一部Userだけか |
| Node | 全Nodeか、特定Nodeだけか |
| 時間 | 常時か、特定時間帯だけか |
| 処理 | 参照か、更新か、Batchか |
| 経路 | 特定Network経路やBackendだけか |
特定APIだけが遅い場合は、そのAPI固有のApplication処理、SQL、外部APIなどを優先して確認できます。一方、ほぼすべてのRequestが同時に遅くなっている場合は、共通のDatabase、Network、Infrastructureなども候補になります。
Response Timeを分解する
利用者から見えるResponse Timeは、複数の処理時間と待ち時間の合計です。

重要なのは、CPUで実際に処理している時間だけでなく、Queue、Lock、I/O、Connection取得、Backend応答などの待ち時間を含めて考えることです。APM、Access Log、Application Log、Databaseの実行統計などを利用できる場合は、各区間の時間を比較します。
Throughputと同時処理数の関係を確認する
Throughputが同じでも、1件あたりの処理時間が長くなると、システム内部に存在する処理中Requestは増えます。安定した状態を単純化すると、Little’s Lawを使って次のように考えられます。
平均同時処理数 ≒ Throughput × 平均処理時間
通常時:500 × 0.1 = 50
遅延時:500 × 1.0 = 500流入量が増えていなくても、Backendの遅延によってThreadやConnectionなどの必要数が急増することがあります。そのため、アクセス数だけでなく処理時間の変化も確認します。
Queueから待ちの場所を探す
パフォーマンス分析では、どこで待っているかを探すことが重要です。

Queueが増えている場所は、そこで処理が滞留していることを示します。ただし、Queueそのものが根本原因とは限りません。その後段の処理能力低下によってQueueが増えている場合があります。
| 待ちの例 | 確認する対象 |
|---|---|
| 接続受付待ち | Listen / Accept Queue、Connection |
| Request処理待ち | Worker、Thread Pool、Executor Queue |
| DB接続取得待ち | Connection Pool |
| SQL待ち | Lock、I/O、CPU、Wait Event |
| CPU実行待ち | Run Queue |
| Storage待ち | I/O Queue、Latency |
CPUボトルネックを分析する
CPUを確認するときは、CPU使用率だけで判断しません。CPUが高い理由と、実行待ちが発生しているかを合わせて確認します。
| 指標 | 確認内容 |
|---|---|
| CPU使用率 | User、System、Idleの割合 |
| Load Average | 実行可能Taskや割り込み不能待ちの増加傾向 |
| Run Queue | CPU実行待ちが増えていないか |
| Context Switch | Thread切り替えが急増していないか |
| Steal Time | 仮想環境でHost側のCPU競合がないか |
| CPU Ready | VMwareでvCPUの実行待ちが増えていないか |
| CPU Throttling | ContainerのCPU Limitに達していないか |
Linuxではtop、mpstat、vmstat、pidstat、perfなどを利用して、System全体からProcess、Thread、Functionへと調査範囲を絞れます。CPU使用率が低いのにResponse Timeが長い場合は、I/O、DB、外部API、Lock、Connection Poolなどの待ちを確認します。
Memoryボトルネックを分析する
MemoryはApplicationだけでなく、JVM、Linux、仮想化基盤まで階層的に確認します。

Linux上のMemory使用率が高いだけで、直ちにMemory不足とは判断できません。Page Cacheを含む使用状況やAvailable Memory、Swap、Page Faultなどを合わせて確認します。JVMではHeap使用量だけでなく、GC回数、GC時間、Pause、Native Memory、Thread数なども確認します。
Storage I/Oボトルネックを分析する
Storage I/Oでは、単純な使用率だけでなく、処理量とLatency、Queueを合わせて確認します。
| 指標 | 確認内容 |
|---|---|
| IOPS | 1秒あたりのI/O回数 |
| Throughput | 単位時間あたりの転送量 |
| Latency | I/O完了までの時間 |
| Queue | I/O待ちの蓄積 |
| await | Linuxから見たI/O完了までの平均時間 |

仮想環境では、Guest OSから見えるI/O遅延だけで原因箇所を断定できない場合があります。HypervisorやStorage側のLatencyも確認し、どのレイヤーで時間が増えているのかを切り分けます。
Networkボトルネックを分析する
Networkでは、帯域だけでなくLatency、Packet Loss、Retransmission、Connectionの状態を確認します。
| 指標 | 確認内容 |
|---|---|
| Bandwidth / Throughput | 回線やNICの処理量に余裕があるか |
| RTT | 通信往復時間が増えていないか |
| Packet Loss | Packet損失が発生していないか |
| Retransmission | TCP再送が増えていないか |
| Connection | 接続数や接続確立に異常がないか |
| SYN / Accept Queue | 接続受付で待ちが増えていないか |
| DNS | 名前解決に時間がかかっていないか |
Network遅延はNetworkだけの問題として現れるとは限りません。外部通信の待ち時間が長くなるとApplication Threadの占有時間が増え、Busy ThreadやQueueの増加として見えることがあります。
Web ServerとTomcatを分析する
Web ServerとTomcatでは、Connection、Worker、Thread、Queue、Timeoutの関係を確認します。
| レイヤー | 主な確認項目 |
|---|---|
| Apache / Nginx | Connection、Worker、KeepAlive、Queue、Timeout |
| Tomcat | Busy Thread、maxThreads、Connection、Executor Queue、Timeout |
| DB Connection Pool | 使用中Connection、空きConnection、取得待ち、Timeout |
たとえばCPU使用率が40%でも、TomcatのBusy Threadが上限に達し、DB Connection Poolも使い切っている場合があります。この状態ではCPUに余裕があることだけを理由に、Application全体にも余裕があるとは判断できません。
Threadを増やせば必ず改善するわけでもありません。Database側の処理能力が限界であれば、同時実行数を増やすことでLock競合やI/O待ちが増え、さらにResponse Timeが悪化する場合があります。
JVMを分析する
Java Applicationでは、Heap、GC、Thread、Native MemoryなどJVM内部の状態も確認します。
| 項目 | 確認内容 |
|---|---|
| Heap | 使用量と増減傾向 |
| GC | 回数、時間、Pause |
| Thread | Thread数、状態、待ち |
| Native Memory | Heap以外のMemory使用量 |
| JFR | CPU、Allocation、Lock、I/Oなどの実行状況 |
GC PauseがResponse Time悪化の時間帯と一致している場合は、GCの影響を詳しく調べます。一方、Heap使用率が高いという事実だけでGCが原因とは判断しません。Thread DumpやJFRを利用すると、ThreadがCPU処理をしているのか、LockやI/O、Databaseなどを待っているのかを確認できます。
Databaseを分析する
DatabaseはWebシステムのResponse Timeに大きな影響を与えるレイヤーです。OracleやPostgreSQLでは実装や確認方法が異なりますが、CPU、Memory、I/O、SQL、Lock、Connection、待ち時間という観点は共通して利用できます。
| 項目 | 確認内容 |
|---|---|
| CPU | DB ProcessがCPUを使い切っていないか |
| Memory / Cache | Cacheの利用状況とI/Oへの影響 |
| Storage I/O | Latency、IOPS、Throughput |
| SQL | 実行時間、実行回数、処理量 |
| Execution Plan | 想定したAccess Pathになっているか |
| Lock | 競合や長時間待ちがないか |
| Connection | 接続数、Pool、上限 |
| REDO / WAL | 書き込みやCommitへの影響 |
| Wait | どのResourceや処理を待っているか |
遅いSQLを見つけたら、SQL文だけを見るのではなく、実行計画、対象データ量、Index、統計情報、I/O、Lockなどを合わせて確認します。
外部APIとBackendの遅延を分析する
自システムのCPUやMemoryに余裕があっても、外部APIやBackendの応答が遅くなることで、システム全体の性能が悪化することがあります。

外部APIを確認するときは、呼び出し回数、接続時間、Response Time、Timeout、Error、Retryなどを確認します。特にRetryが設定されている場合、障害時にRequest数が増幅することがあります。
仮想化基盤とContainerを分析する
Guest OSやApplicationに異常が見つからない場合でも、その下の仮想化基盤やContainerのResource制限が性能へ影響している場合があります。
| 環境 | 主な確認項目 |
|---|---|
| VMware | CPU Ready、Co-Stop、NUMA、Ballooning、Storage Latency、Network |
| Container | CPU Limit、CPU Throttling、Memory Limit、OOM、Pod / Node Resource |
| Host | CPU、Memory、Storage、Network、他VM・Containerとの競合 |
仮想環境では、Guest OSのCPU使用率だけでなく、vCPUが実際に実行できているかを確認します。また、同じHost上の別Workloadから影響を受ける場合もあります。
時系列で相関を見る
性能問題を分析するときは、複数のMetricを同じTimelineで比較します。

重要なのは、同じ時間に変化したMetricを並べるだけではなく、どの変化が先に発生したのかを見ることです。たとえばDB I/O Latencyの増加後にSQL Response Timeが悪化し、その後にDB Connection PoolとTomcat Threadが滞留したのであれば、DB側の遅延が前段へ波及した可能性を検討できます。
ただし、時間的な相関だけで因果関係を断定することはできません。仮説を立てたうえで、追加のMetricやLog、再現試験などで確認します。
正常時と比較する
障害時の値だけを見ると、その値が異常なのか判断できない場合があります。同じ機能、同程度のWorkload、同じ時間帯など、比較可能な正常時データがあると分析しやすくなります。
| 項目 | 正常時 | 遅延時 |
|---|---|---|
| RPS | 500 | 500 |
| p95 | 200 ms | 1.5 s |
| CPU | 50% | 55% |
| Busy Threads | 60 | 190 |
| DB I/O Latency | 5 ms | 80 ms |
この例ではRPSとCPUに大きな変化がない一方、Busy ThreadsとDB I/O Latencyが増えています。この差分を手掛かりに、DatabaseやStorage側へ調査範囲を絞れます。正常時のBaselineを日頃から保存しておくことは、障害時の分析に有効です。
変更点を確認する
性能問題が突然発生した場合は、発生時刻の前後に何が変わったかを確認します。Application Release、Middleware設定、SQL、Database統計情報、Infrastructure、Workload、障害などが対象です。
変更があったことと性能問題の原因であることは同じではありませんが、調査候補を絞る重要な情報になります。
End-to-Endトラブルシューティングの進め方
ここまでの内容を、実際のトラブルシューティングの流れとして整理します。

すべての障害でこの順番を固定的に使う必要はありません。たとえば特定SQLだけが遅いことが分かっているなら、Database分析から始めた方が効率的です。重要なのは、観測した事実と仮説を分け、仮説を計測によって検証することです。
仮説を立てて検証する
パフォーマンス分析では、Metricを眺め続けるだけではなく、観測結果から仮説を立てます。

たとえば「DB I/O Latencyの増加がSQLを遅くし、Tomcat Threadの滞留を引き起こしている」という仮説を立てた場合、DBのWait、SQL実行時間、Storage Latency、Tomcat Busy Threadの時間推移を比較します。
対策後には必ず再測定し、Response TimeやThroughputが改善したか、別のボトルネックへ移動していないかを確認します。
パフォーマンス分析で避けたいこと
| 避けたい分析 | 問題 |
|---|---|
| CPU使用率だけで判断する | I/OやLock、Backend待ちを見逃す |
| 平均値だけを見る | 一部の遅いRequestを見逃す |
| いきなりParameterを変更する | 原因と効果の関係が分からなくなる |
| ThreadやConnectionをとりあえず増やす | 後段の負荷や競合を増やす場合がある |
| 複数の設定を同時に変更する | どの変更が効いたのか判断しにくい |
| Applicationだけを見る | DB、Network、Infrastructureの問題を見逃す |
| 障害時のデータだけを見る | 正常時との差分を判断できない |
| 相関をそのまま原因と判断する | 同時に変化した別要因を見逃す |
性能問題では、目立つMetricが根本原因とは限りません。Queue増加やThread枯渇は、さらに後段で発生した遅延の結果として現れることがあります。
Performance Engineeringのサイクル
パフォーマンス分析は、障害が発生したときだけ行う作業ではありません。性能要件、性能設計、性能試験、監視、分析、改善を繰り返すことで、システムの性能を継続的に管理します。

性能試験で取得した正常時のMetricは、本番障害時のBaselineとしても利用できます。また、本番で発見した性能問題を次回の性能試験シナリオへ反映することで、同じ問題の再発防止につなげられます。
まとめ
Webシステムの性能問題は、CPU、Memory、Storage、Network、Web Server、Tomcat、JVM、Databaseなど、複数のレイヤーが関係して発生します。
そのため、最初から特定のResourceやComponentを原因と決めつけるのではなく、Response Time、Throughput、Error Rateから現象を把握し、影響範囲と処理経路を確認します。
次にResponse Timeを処理時間と待ち時間へ分解し、Queue、Thread、Connection、I/O、Lock、Backendなど、どこで処理が滞留しているのかを確認します。
さらにCPU、Memory、I/O、NetworkからWeb Server、Tomcat、JVM、Database、仮想化基盤まで必要なレイヤーを掘り下げ、正常時との比較や時系列の相関からボトルネック候補を絞ります。
重要なのは、観測した事実から仮説を立て、Metric、Log、Trace、性能試験などで検証することです。対策後には再測定し、本当に性能が改善したかを確認します。
Performance Engineeringは、性能要件を決めて終わるものではありません。設計、試験、監視、分析、改善、再検証を繰り返すことで、Webシステム全体の性能を継続的に維持していきます。
参考文献
- Brendan Gregg 著、長尾高弘 訳『詳解 システム・パフォーマンス 第2版』オライリー・ジャパン。
- Brendan Gregg, Systems Performance: Enterprise and the Cloud, 2nd Edition, Addison-Wesley Professional.


コメント