【図解】Webシステムの性能分析・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 RateTimeoutや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 QueueCPU実行待ちが増えていないか
Context SwitchThread切り替えが急増していないか
Steal Time仮想環境でHost側のCPU競合がないか
CPU ReadyVMwareでvCPUの実行待ちが増えていないか
CPU ThrottlingContainerの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を合わせて確認します。

指標確認内容
IOPS1秒あたりのI/O回数
Throughput単位時間あたりの転送量
LatencyI/O完了までの時間
QueueI/O待ちの蓄積
awaitLinuxから見たI/O完了までの平均時間

仮想環境では、Guest OSから見えるI/O遅延だけで原因箇所を断定できない場合があります。HypervisorやStorage側のLatencyも確認し、どのレイヤーで時間が増えているのかを切り分けます。

Networkボトルネックを分析する

Networkでは、帯域だけでなくLatency、Packet Loss、Retransmission、Connectionの状態を確認します。

指標確認内容
Bandwidth / Throughput回線やNICの処理量に余裕があるか
RTT通信往復時間が増えていないか
Packet LossPacket損失が発生していないか
RetransmissionTCP再送が増えていないか
Connection接続数や接続確立に異常がないか
SYN / Accept Queue接続受付で待ちが増えていないか
DNS名前解決に時間がかかっていないか

Network遅延はNetworkだけの問題として現れるとは限りません。外部通信の待ち時間が長くなるとApplication Threadの占有時間が増え、Busy ThreadやQueueの増加として見えることがあります。

Web ServerとTomcatを分析する

Web ServerとTomcatでは、Connection、Worker、Thread、Queue、Timeoutの関係を確認します。

レイヤー主な確認項目
Apache / NginxConnection、Worker、KeepAlive、Queue、Timeout
TomcatBusy 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
ThreadThread数、状態、待ち
Native MemoryHeap以外のMemory使用量
JFRCPU、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、待ち時間という観点は共通して利用できます。

項目確認内容
CPUDB ProcessがCPUを使い切っていないか
Memory / CacheCacheの利用状況とI/Oへの影響
Storage I/OLatency、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制限が性能へ影響している場合があります。

環境主な確認項目
VMwareCPU Ready、Co-Stop、NUMA、Ballooning、Storage Latency、Network
ContainerCPU Limit、CPU Throttling、Memory Limit、OOM、Pod / Node Resource
HostCPU、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、同じ時間帯など、比較可能な正常時データがあると分析しやすくなります。

項目正常時遅延時
RPS500500
p95200 ms1.5 s
CPU50%55%
Busy Threads60190
DB I/O Latency5 ms80 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.

コメント