パフォーマンス監視メトリクス入門

# 0. はじめに
## 0.1 コンピュータシステムの構成要素
コンピュータシステム、例えばAmazon EC2のようなサーバーは、以下のようないくつかの主要なリソース（計算資源）によって構成されている。

1. CPU
1. メモリ
1. ディスク
1. ネットワーク

## 0.2 パフォーマンス負荷とメトリクス

Webアプリケーションを運用していると、何かしらの原因で、サーバーの **スループット（一定時間あたりのデータ処理量）** が減少したり、 **レイテンシ（データを送受信する時間）** が増加してしまう場面がある。
これをパフォーマンスが悪化したという。

パフォーマンスが悪化した場合、各リソースの **メトリクス（負荷の計測結果）** を確認し、**ボトルネック（パフォーマンス悪化の原因となっているリソース）** を特定することでパフォーマンスの改善、チューニングに繋げることができる。

ここでは、CPU使用率、メモリ使用率、ディスクI/O、ロードアベレージといったメトリクスについて見ていく。

# 1. CPU使用率
## 1.1 CPU使用率の定義

- 特定の期間中に **CPUがビジー状態であった時間の割合** 。
- CPUが持つ計算リソースをどの程度の割合利用しているのかを指す。

## 1.2 CPU使用率の理解

- アプリケーションサーバー（APサーバー）は、データベースから取得したデータを加工してクライアントに渡す役割を担い、大規模なI/Oは発生しないため、主にCPU負荷が中心となる（**CPUバウンドなサーバー**）。
- CPU使用率には、ユーザーモード（ `us` ）での使用率と、カーネルモード（ `sy` ）での使用率がある。
-  **ユーザーモード**
    -  通常のアプリケーションプログラムが実行される制限されたモード。
    -  直接ハードウェアを操作することはできないため、カーネルモードに切り替える必要がある（**システムコール**）。
-  **カーネルモード（システムモード）**
    -  OSのカーネルが実行される特権レベルのモード。
    -  ハードウェアの制御や直接操作が可能。
- ユーザースレッド
    - ユーザーモードで管理され、動作するスレッド。
- カーネルスレッド
    - カーネルモードで管理され、動作するスレッド。
- **ビジー状態**
    - CPUがユーザースレッドまたはカーネルスレッドを実行している時間のこと
    - 例えば、Webアプリケーションや、プロセスのfork、コンテキストスイッチが実行されている状態。
- **アイドル状態**
    - CPUが実行すべきタスクを持たず、待機状態にあること。

## 1.3 CPU使用率の計測方法

### 1.3.1 top コマンド

- 画面の上部に「システム全体」の情報、下部に「各プロセス」の情報が表示される。
- 「各プロセス」の情報において、`%CPU` 列にCPU使用率が表示される。

```bash
$ top
top - 15:24:25 up 1 day,  3:49,  3 users,  load average: 2.50, 2.20, 2.10
Tasks: 200 total,   2 running, 198 sleeping,   0 stopped,   0 zombie
%Cpu(s): 80.0 us,  5.0 sy,  0.0 ni, 10.0 id,  5.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :  7972.0 total,   1024.0 free,  6144.0 used,  804.0 buff/cache
MiB Swap:  2047.0 total,   2047.0 free,     0.0 used.  2048.0 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
 1234 root      20   0  500000  12000   4500 R  75.0   0.2  120:15.45 python
 1235 mysql     20   0 1600000  80000   4500 S  10.0   1.0   50:15.45 mysqld
 1236 user      20   0  200000   6000   2000 S   5.0   0.1    5:55.55 sshd
 1237 user      20   0  100000   3000   1000 S   3.0   0.0    3:45.45 bash
 1238 user      20   0  150000   5000   1500 R   2.0   0.1    1:00.00 top
```

- 出力
    - `%Cpu(s)` : 現在のCPU使用率の詳細。
        - `us`  : ユーザーモードによるCPU使用率。ユーザースレッドがCPUを使用している時間の割合。
        - `sy` : システムモード（カーネルモード）によるCPU使用率。カーネルスレッドがCPUを使用している時間の割合。
        - `ni` : スケジューリングの優先度（nice値）が変更されたユーザースレッドが動作する時間の割合。
        - `id` : CPUがアイドル状態で消費した時間の割合。
        - `wa` : CPUが「ディスクI/O待ちのために」アイドル状態で消費した時間の割合。
        - `hi` : ハードウェア割り込みの処理時間の割合。
        - `si` : ソフトウェア割り込みの処理時間の割合。
        - `st` : 他の仮想CPUの計算で待たされた時間（Steal時間）の割合。

### 1.3.2 sar コマンド

- システムのCPU使用率を一定間隔で表示する。

```bash
$ sar -u -P ALL 1 3
15:23:12        CPU     %user     %nice   %system   %iowait    %steal     %idle
15:23:13        all      44.32      0.00      5.08      0.27      0.00     50.32
15:23:13          0      46.12      0.00      5.53      0.51      0.00     47.83
15:23:13          1      42.53      0.00      4.53      0.00      0.00     52.93
15:23:14        all      43.00      0.00      4.99      0.25      0.00     51.76
15:23:14          0      45.50      0.00      5.50      0.50      0.00     48.50
15:23:14          1      40.50      0.00      4.50      0.00      0.00     55.00
Average:        all      44.88      0.00      5.31      0.26      0.00     49.88
Average:          0      45.31      0.00      5.51      0.51      0.00     48.67
Average:          1      41.02      0.00      4.52      0.00      0.00     54.46
```

- オプション
    - `-u` : CPU使用率を表示。
    - `-P ALL` : 全CPUコアの使用率を表示。
    - `1` : 1秒ごとに結果を表示。
    - `3` : 3回のサンプルを取得。
- 出力
    - `user` : ユーザースレッドが動作する時間の割合。
    - `nice` : スケジューリングの優先度（nice値）が変更されたユーザースレッドが動作する時間の割合。
    - `system` : カーネルスレッドが動作する時間の割合。
    - `iowait` : CPUが「ディスクI/O待ちのために」アイドル状態で消費した時間の割合。
    - `steal` : 他の仮想CPUの計算で待たされた時間（Steal時間）の割合。
    - `idle` : CPUがアイドル状態で消費した時間の割合。

# 2. メモリ使用率
## 2.1 メモリ使用率の定義

- システム全体の **物理メモリ（RAM）のうち、どの程度が使用されているかを示す割合** 。
    - メモリ使用率 =  使用中のメモリ ÷ 総メモリ × 100

## 2.2 メモリ使用率の理解

- メモリには物理メモリと仮想メモリが存在する。
- **物理メモリ（RAM）**
    - コンピュータのハードウェアに搭載された実際のメモリ。
- **仮想メモリ**
    - プロセスごとに提供される、仮想アドレス空間。
    - 仮想メモリにより、各プロセスは物理メモリを超える大きなメモリ空間を使用でき、自分専用の大きなメモリ空間を持つことができる。
- メモリが不足すると、スワップが発生し、システムの応答速度が低下する。
- **メモリリーク**
    - プログラムが使用したメモリを適切に解放しない場合に発生し、これによりシステムのパフォーマンスが低下する。
    - メモリリークを検出するためには、プロファイリングツールやデバッグツールを使用する。
- **スワップ**
    - 物理メモリが不足したとき、補助記憶装置として使用されるディスク（HDDやSSD）上の領域のこと。
    - 仮想メモリの一部として機能することで、より多くのメモリを利用できるようにする。
    - **スワップイン**
        - 必要に応じて、スワップ領域に移動されたデータやプログラムを再びRAMに戻すこと。
    - **スワップアウト**
        - RAMが不足したときに、使用頻度が低いデータやプログラムの一部をスワップ領域に移動し、物理メモリを解放すること。
- **ページング**
    - 物理メモリと仮想メモリは、 **ページ** という固定サイズのブロックに分割される。
    - **ページテーブル** により、物理メモリのページと仮想メモリのページが対応する。
    - ページングにより、仮想メモリ空間が物理メモリより大きい場合でも、メモリを効率的に管理できる。
    - **ページイン**
        - ページフォールトが発生したとき、OSが仮想メモリ空間の対応するページをディスクから物理メモリに読み込むこと。
        - ページのスワップイン。
    - **ページアウト**
        - 仮想メモリ空間のページが物理メモリに保持されており、システムが物理メモリを他のプロセスやページのために解放する必要がある場合、OSが仮想メモリ空間の対応するページをディスクに移動すること。
        - ページのスワップアウト。
    - **ページフォールト**
        - プロセスが仮想メモリ上のページにアクセスし、そのページが物理メモリに存在しないことを示すシグナル。
        - ページフォールトが発生すると、ページインが行われる。

スワップアウトとスワップインのイメージ:

![swapping-swap-in-swap-out-os-operating-systems.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54467/7b6296f1-a468-7f27-a135-8f19de758d94.png)

[Swapping, Swap in, Swap out in Operating Systems (OS) - T4 Tutorials](https://t4tutorials.com/swapping-swap-in-swap-out-in-operating-systems-os/)

ページフォールトのイメージ:
※ ②がページフォールトの発生、④がページインの処理を表す。

![121-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54467/ddbbf4be-069b-b723-1ba7-d951e544a2b2.png)

## 2.3 メモリの計測方法
### 2.3.1 free コマンド

- システムのメモリ使用状況を表示します。

```bash
$ free -h
              total        used        free      shared  buff/cache   available
Mem:           7.8G        6.0G        200M        104M        1.6G        1.5G
Swap:          2.0G        500M        1.5G
```

- オプション
    - `-h` : メモリの単位を人間が読みやすい形式（GB、MB、KB）で表示。
        - `-g` : メモリの単位をギガバイトで表示。
        - `-m` : メモリの単位をメガバイトで表示。
        - `-b` : メモリの単位をバイトで表示。
- 出力
    - 行
        - `Mem` : システムの物理メモリ（RAM）の使用状況を示す。
        - `Swap` : スワップ領域の使用状況を示す。
    - 列
        - `total` : システム全体のメモリ容量（MB）。
        - `used` : 使用中のメモリ容量（MB）。
        - `free` : 空きメモリ容量（MB）。
        - `shared` : 複数のプロセス間で共有されているメモリ容量（MB）。
        - `buff/cache` : バッファとキャッシュとして使用されているメモリ容量（MB）。
        - `available` : 新しいプロセスによって割り当て可能なメモリ容量（MB）。

### 2.2.2 vmstat コマンド

- システムの仮想メモリ、プロセス、I/O、CPUなどの統計情報を表示する。

```bash
$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 1  0    500  206788 201448 3172164    2    3     2     1    0    1  1  0 99  0  0
 0  0    500  206788 201448 3172164    0    0     0     0    0    0  0  0 100 0  0
```

- オプション
    - `1` : 1秒ごとに結果を表示。
    - `5` : 5回のサンプルを取得。

## 2.4 スワップ発生状況の確認
### 2.4.1 sar -W コマンド

- `sar` に `-W` オプションを指定すると、スワップの発生状況を確認できる。

```bash
$ sar -W 1 3
15:23:12    pswpin/s  pswpout/s
15:23:13       0.50       1.20
15:23:14       0.30       1.00
15:23:15       0.20       0.80
Average:       0.33       1.00
```

- オプション
    - `-W` : スワップの発生状況を表示。
    - `1` : 1秒ごとに結果を表示。
    - `3` : 3回のサンプルを取得。
- 出力
    - `pswpin/s` : 1秒間にスワップインしているページ数。
    - `pswpout/s` : 1秒間にスワップアウトしているページ数。

# 3. ディスクI/O
## 3.1 ディスクI/Oの定義

- システムが **ディスク（HDDやSSD）へのデータの読み書きを行う速度や、ディスクの応答時間** を示す指標。

## 3.2 ディスクI/Oの理解
- データベースサーバー（DBサーバー）やファイルサーバーは、データが大規模になるとディスクアクセスが頻繁に行われ、主にI/O負荷が中心となる（**I/Oバウンドなサーバー**）。
- 高いディスクI/Oは、ディスクのパフォーマンスが良好であることを示すが、極端に高い場合はボトルネックとなる可能性がある。

## 3.2 I/Oの計測方法

### 3.2.1 iostat コマンド

- CPUおよびI/Oデバイスの使用状況を報告する。

```bash
$ iostat
Linux 4.15.0-45-generic (hostname)     02/22/2019     _x86_64_    (4 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           1.28    0.00    0.30    0.10    0.00   98.32

Device             tps    kB_read/s    kB_wrtn/s    kB_read    kB_wrtn
sda               1.20        15.00        10.00    234567     123456
```

- 出力
    - `avg-cpu` : CPU使用率の平均。
        - `tps` : 一秒あたりのI/O転送数。
        - `kB_read/s` : 一秒あたりの読み取り量（キロバイト）。
        - `kB_wrtn/s` : 一秒あたりの書き込み量（キロバイト）。
        - `kB_read` : 累積読み取り量（キロバイト）。
        - `kB_wrtn` : 累積書き込み量（キロバイト）。

## 3.3 高I/O負荷のトラブルシューティング

- 高I/O負荷の原因を特定するためには、どのプロセスが大量のI/O操作を行っているかを確認する。
    - 例えば、データベースのインデックス作成やバックアップジョブが原因となることがある。
- `iotop` コマンドを使用することで、リアルタイムで各プロセスのI/O操作を監視できる。

### 3.3.1 iotop コマンド

- 各プロセスのI/O操作をリアルタイムで表示する。

```bash
$ sudo iotop
Total DISK READ: 0.00 B/s | Total DISK WRITE: 0.00 B/s
PID USER       DISK READ DISK WRITE COMMAND
1234 root        15.00 B/s  10.00 B/s my_process
```

- 出力
    - `DISK READ` : ディスクの読み取り速度。
    - `DISK WRITE` : ディスクの書き込み速度。
    - `COMMAND` : 実行中のコマンド。

# 4. ロードアベレージ
## 4.1 ロードアベレージの定義

- 過去の一定期間における **実行中のプロセス数と、実行待ちのプロセス数*の平均値** のこと。
- ロード = load = 負荷。
- **CPUロード（CPU負荷）** 
    - **実行中のプロセス数と、実行待ちのプロセス数** のこと。

## 4.2 ロードアベレージの理解

- 通常、1分、5分、15分の3つの時間枠で表示される。
    - **1分平均** : 「短期的な負荷」、直近1分間のシステム負荷を示す。
    - **5分平均** : 「中期的な負荷の平均値」、直近5分間の負荷の傾向を示す。
    - **15分平均** : 「長期的な負荷の平均値」、直近15分間の負荷の傾向を示す。
- ロードアベレージの値がCPUコアの数以下であれば、システムは効率的にタスクを処理できていると言える。
- 逆に、ロードアベレージの値がCPUコアの数を超える場合、システムは過負荷状態にある可能性がある。
- 例
    - ロードアベレージが「1」の時：
        - シングルコアシステムでは、CPUが常に忙しく、100%のリソースが利用されている。
        - デュアルコアシステムでは、50%のリソースが使用されており、残りの50%がアイドル状態。
        - クアッドコアシステムでは、25%のリソースが使用されていることを意味する。
    - ロードアベレージが「2」の時：
        - シングルコアシステムでは、システムが過負荷状態にあり、待機しているプロセスが存在することを示す。
        - デュアルコアシステムでは、100%のリソースが利用されており、過負荷ではない状態。
        - クアッドコアシステムでは、50%のリソースが使用されていることを意味する。


[Understanding Linux CPU Load - when should you be worried? - SCOUT MONITORING](https://b.hatena.ne.jp/entry/s/scoutapm.com/blog/understanding-load-averages)

## 4.3 ロードアベレージの確認方法

先述の `top` コマンドを使用することもできる。

### 4.3.1 uptime コマンド

- システムの稼働時間とロードアベレージを表示する。

```bash
$ uptime
15:24:10 up 1 day,  3:49,  3 users,  load average: 0.58, 0.52, 0.48
```

- 出力
    - `up 1 day, 3:49` : システムの稼働時間。
    - `3 users` : 現在ログインしているユーザー数。
    - `load average` : 1分、5分、15分のロードアベレージ。

### 4.3.2. sar -q コマンド

- ランキューに溜まっているプロセスの数、システム上のプロセスサイズ、ロードアベレージを表示する。

```bash
$ sar -q 1 3
15:23:12 runq-sz  plist-sz   ldavg-1   ldavg-5  ldavg-15   blocked
15:23:13       1       236      2.58      2.52      2.48         0
15:23:14       0       236      2.60      2.55      2.50         1
15:23:15       0       236      2.62      2.57      2.52         0
Average:       0.33    236      2.60      2.55      2.50         0.33
```

- オプション
    - `-q` : ロードアベレージを表示。
    - `1` : 1秒ごとに結果を表示。
    - `3` : 3回のサンプルを取得。
- 出力
    - `runq-sz` : ランキューに溜まっているプロセスの数。
    - `plist-sz` : システム上のプロセスサイズ。
    - `ldavg-1` : 1分間のロードアベレージ。
    - `ldavg-5` : 5分間のロードアベレージ。
    - `ldavg-15` : 15分間のロードアベレージ。
    - `blocked` : ブロックされているプロセスの数。

## 4.4. CPU使用率、I/O待ち率のどちらがボトルネックかを探る

- まず最初にロードアベレージを見て、ロードアベレージが高い場合、次に「CPU使用率」と「I/O待ち率」を確認して、どちらがボトルネックになっているかを調べる。
    - 先述の `sar` コマンドを使用して、「%user」（ユーザモードでの使用率）と「%system」（システムモードでの使用率）が高ければCPUリソースが不足しており、「%iowait」（I/O待ち率）が高ければ、I/Oがボトルネックであると判断できる。
- 「I/O負荷」が高い場合、プログラムの入出力が多く、ディスクアクセスが発生している可能性がある。
    - 先述の `iostat` コマンドで確認する。
- 「I/O待ち」が多い場合、スワップが発生していないかも確認する。
    - 先述の `vmstat` コマンドを使用する。

# 6. 本記事で触れたLinuxコマンド

- `top` ：CPU使用率やプロセス情報の確認。
- `sar` ：CPU使用率の定期的な計測。System Activity Reporter。
- `free` ：メモリ使用状況の確認。
- `vmstat` ：メモリ、I/O、CPUの統計情報の表示。
- `iostat` ：CPUおよびI/Oデバイスの使用状況の報告。
- `iotop` ：プロセスごとのI/O操作の監視。
- `uptime` ：システムの稼働時間とロードアベレージの確認。

# 参考

- [［Web開発者のための］大規模サービス技術入門―データ構造、メモリ、OS、DB、サーバ/インフラ](https://gihyo.jp/book/2010/978-4-7741-4307-1)
- [［24時間365日］サーバ/インフラを支える技術 ……スケーラビリティ、ハイパフォーマンス、省力運用](https://gihyo.jp/book/2008/978-4-7741-3566-3)
- [達人が教えるWebパフォーマンスチューニング〜ISUCONから学ぶ高速化の実践](https://gihyo.jp/book/2022/978-4-297-12846-3)
- [詳解 システム・パフォーマンス 第2版](https://www.oreilly.co.jp/books/9784814400072/)
- [Linuxシステム入門 - Qiita](https://qiita.com/yokoto/items/a2536bd1b81b42479bba)
- [システムの負荷の原因を切り分ける方法 - Qiita](https://qiita.com/k0kubun/items/8ab1dfa7c0359d8e618d)
- [LinuxのI/OやCPUの負荷とロードアベレージの関係を詳しく見てみる - Qiita](https://qiita.com/kunihirotanaka/items/21194f77713aa0663e3b)