サーバが重い時のボトルネック調査手順と解決方法まとめ

バックエンド

サーバが重いときのボトルネックの見つけ方と対応方法について紹介します。

ボトルネックの種類

ボトルネックには以下の種類があります。

ボトルネックの種類
  • CPU使用率
  • ディスクI/O
  • メモリ使用量
  • TCPコネクション
  • 外的要因

外的要因とは重くなっているサーバ自身ではなくリモートホストやDNSなどに問題があるケースです。

サーバの状態とボトルネックの対応

サーバが重い時のサーバの状態と、ボトルネックの対応をまとめると以下のようになります。

  • ロードアベレージが高い
    • ユーザーモードのCPU使用率が高い
      • アプリケーション起因のCPU使用率がボトルネック
    • システムモードのCPU使用率が高い
      • アプリケーション以外の要因で生じるCPU使用率がボトルネック
    • I/O待ちが長い
      • スワップが発生している
        • メモリ不足がボトルネック
      • スワップが発生していない
        • ディスクI/Oがボトルネック
  • ロードアベレージが低い
    • TCPコネクション数が多い
      • TCPコネクションがボトルネック
    • TCPコネクション数は多くない
      • 外的要因がボトルネック

ボトルネックを発見するための具体的な手順

ボトルネックを見つけるには以下の手順でサーバを調査するとよいです。

ボトルネックの調査手順
  1. ロードアベレージの確認
  2. ロードアベレージが高い場合はCPU使用率とI/O待ちを確認
  3. I/O待ちが長い場合はスワップの有無を確認
  4. ロードアベレージが低い場合はTCPコネクション数を確認

以下では各手順について解説をします。

ロードアベレージの確認

ロードアベレージが高い場合は『CPU使用率』『ディスクI/O』『メモリ使用量』のいずれかが、低い場合は『TCPコネクション』もしくは『外的要因』がボトルネックです。

ロードアベレージはtopload averageなどで確認できます。

top - 14:27:01 up 0 min,  1 user,  load average: 1.65, 0.50, 0.17
Tasks: 119 total,   1 running, 118 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  1.0 sy,  0.7 ni, 98.0 id,  0.3 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :   499816 total,    63044 free,   223308 used,   213464 buff/cache
KiB Swap:  2097148 total,  2047708 free,    49440 used.   243264 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
 1483 netdata   39  19   95136  29752   2004 S  1.0  6.0   0:00.96 netdata
 1635 root      39  19    5692   1732    172 S  0.7  0.3   0:00.39 apps.plugin
 1623 netdata   39  19    1736    780    484 S  0.3  0.2   0:00.03 bash

目安として、ロードアベレージはCPU1コアあたり3~5程度あると高いといえます。
もしCPUのコア数が2の場合は6~10程度だと高ロードアベレージということになります。

CPUのコア数はgrep cpu.cores /proc/cpuinfo | sort -uで確認ができます。

$ grep cpu.cores /proc/cpuinfo | sort -u
cpu cores   : 1

ロードアベレージが高い場合はCPU使用率とI/O待ちを確認

I/O待ちが長い場合は『ディスクI/O』もしくは『メモリ使用量』が、CPU使用率が高い場合は『CPU使用率』がボトルネックです。

『CPU使用率』はさらにユーザーモードとシステムモードに細分化されます。
ユーザーモードのCPUとはアプリケーションの実行に関するCPU、システムモードのCPUとはカーネルの実行に関するCPUのことをいいます。
つまり、ユーザーモードのCPU使用率が高い場合はアプリケーション起因、システムモードのCPU使用率が高い場合はアプリケーション以外の要因で生じるCPUがボトルネックです。

システムモードのCPU使用率が高い場合はI/Oも高い可能性があるので、あわせて確認をしておくとよいでしょう。

CPU使用率とI/O待ちはsar -u [時間刻み幅]dstat -aなどで確認できます。
たとえばsar -uであれば%user%system%iowaitの項目を確認します。

$ sar -u 2
Linux 4.4.0-210-generic (160-251-11-228)    06/20/2021  _x86_64_    (1 CPU)

02:29:00 PM     CPU     %user     %nice   %system   %iowait    %steal     %idle
02:29:02 PM     all      0.00      0.50      1.50      0.00      0.00     98.00
02:29:04 PM     all      0.00      0.51      1.01      0.00      0.00     98.48
02:29:06 PM     all      0.00      1.01      0.51      0.00      0.00     98.48

I/O待ちが長い場合はスワップの有無を確認

スワップが発生している場合は『メモリ使用量』が、発生していない場合は『ディスクI/O』がボトルネックです。

スワップ発生の有無はsar -W [時間刻み幅]vmstat [時間刻み幅]dstat --swapなどで確認できます。
たとえばsar -Wであればpswpin/spswpout/sの項目を確認します。

$ sar -W 2
Linux 4.4.0-210-generic (160-251-11-228)    06/20/2021  _x86_64_    (1 CPU)

02:30:08 PM  pswpin/s pswpout/s
02:30:10 PM      0.00      0.00
02:30:12 PM      0.00      0.00
02:30:14 PM      0.00      0.00

ロードアベレージが低い場合はTCPコネクション数を確認

TCPコネクション数が多い場合は『TCPコネクション』、少ない場合は『外的要因』がボトルネックです。

TCPコネクション数はnetstatssなどで確認できます。
たとえばnetstatであればnetstat -an | wc -lの数を確認します。

$ netstat -an | wc -l

111

目安として、値が数万の場合はTCPコネクション数が多いといえます。

ボトルネックの解消方法

ボトルネックを解消するための具体的な方法について紹介します。

アプリケーション起因のCPU使用率がボトルネックの場合

アプリケーションの実装に問題があるためCPU使用率が高くなっている状態です。

topなどでCPU使用率の高いプロセスを特定し、当該プロセスの実装に問題がないかプログラムのロジックを再確認します。
たとえばNew RelicやDatadogをはじめとしたアプリケーションモニタリングツール(APM)を利用することでボトルネックとなっているロジックの特定ができます。

アプリケーション以外の要因で生じるCPU使用率がボトルネックの場合

プロセスやスレッドの動作、もしくはI/O待ちが原因でCPU使用率が高くなっている状態です。

アプリケーション以外の要因でCPU使用率が高い場合、I/O待ちも長いケースが多いです。
ですので、I/O待ちもあわせて確認をし、ディスクI/Oがボトルネックの場合はそちらの対応をします。

I/O待ちに問題がない場合はtopなどを利用して高頻度でforkされているプロセスがないか確認をし、存在する場合はプロセスの改善をします。

メモリ不足がボトルネックの場合

メモリが足りないためメモリ上にデータがのり切らずスワップが発生してしまい、その結果I/O待ちが長くなっている状態です。

まずはtopなどでメモリ消費量が多いプロセスを特定し、当該プロセスでのメモリの使い方を改善できないか・使い方は適切であるのか確認します。
たとえば大容量のデータを一度にメモリへのせる実装をしているのであれば改善の余地があります。

メモリの使い方に問題がないのであれば単にメモリが足りていないのが原因であるためメモリ増強を検討します。

ディスクI/Oがボトルネックの場合

読み書きの頻度が高いためスワップこそ発生していないもののディスクの処理が追いついていない状態です。

ディスクI/Oの使用状況はsar -b [時間刻み幅]などで確認ができます。
pidstat -dなどで読み書きの頻度が高いプロセスを特定し、キャッシュを導入したりプログラムを修正したりすることで読み書きの頻度を減らすようにします。

TCPコネクションがボトルネックの場合

許容量以上のコネクションが作成されている状態です。

コネクションを分散するなどしてコネクション数を減らす対策をします。

さいごに

Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!

参考