「達人が教える Webパフォーマンスチューニング ~ISUCONから学ぶ高速化の実践」をもとに自社サービスの状態を評価してみる

# 「達人が教える Webパフォーマンスチューニング ~ISUCONから学ぶ高速化の実践」をもとに自社サービスの状態を評価してみる

自社で運営しているwebサービス(Rails)のパフォーマンスを上げようということでNew Relicを導入したものの「はて、何を見たらいいのやら」状態でしたので
本著の内容をもとに自社サービスの状態はどのようになっているか、どのような改善アクションを取れるかを評価してみました。

具体的にはそれぞれの章を読み終えた後に自社サービスの状況と改善点をまとめ、自社サービスの状態を ○ × △ のいずれかに分類してみました。

## 1章 高速なWebサービスとは: ○
### 概要
- 高速なWebサービスの定義とは何か？
- ユーザー体験における「高速」とは具体的にどのような状態か？

### 自社サービスの状況
- ユーザーのページロード時間はどの程度か？（Googleが重要としている三つの指標）
  - INP (Interaction to Next Paint)
ユーザーがページ内で行った操作（クリックや入力など）に対して、次の画面更新（ペイント）が行われるまでの時間の指標。
  - LCP (Largest Contentful Paint)
ページ内で最も大きなコンテンツ（画像や見出しなど）が表示されるまでの時間。ページの主要コンテンツが読み込まれたタイミングを示す。
 
   - CLS (Cumulative Layout Shift)
ページのレイアウトが意図せずどれだけ動いたかを測る指標。

New Relicを導入しているため以下のように確認できる
![スクリーンショット_2025-03-01_22_59_33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427079/76054421-ab36-4bc9-8678-c32d6a21a430.png)

またNew Relicでは以下のようにしてLCPを確認することもできる
```SQL
SELECT percentile(largestContentfulPaint, 75), 
       average(elementSize/1024) as 'Element Size(MB)'
FROM PageViewTiming 
WHERE timingName='largestContentfulPaint' AND pageUrl LIKE 'https://sys.furusato-baizo.com/admins/%' 
FACET pageUrl, elementUrl
WITH TIMEZONE 'Asia/Tokyo' SINCE '2025-02-25' UNTIL '2025-02-26'
```

- サーバーのレスポンス速度は適切か？
  - どれくらいを適切とするか決めていない
- 高速化の指標を設定しているか？
  - していない

### 改善点・対応方針
- 目標とするパフォーマンス指標を定めていないので定める
- LCPがが悪いのでボトルネックを特定し、優先的に対策をとる

---

## 2章 モニタリング: ○
### 概要
- パフォーマンスを観測するための手法とは？
- モニタリングの重要性と導入手順

### 自社サービスの状況
- どのような監視ツールを導入しているか？
  - APM
    - New Relic
    - AWSのCloudWatch
  - ログ管理
    - New Relic
- 主要なパフォーマンス指標をどのように監視しているか？
  - そこまでできていない 
- ボトルネックの検出方法は適切か？
  - New Relicでできるがそこまで対応できていない

### 改善点・対応方針
- アラート設定を適宜追加していく
- 監視対象のアクションを決定しモニタリングする

---

## 3章 基礎的な負荷試験: ×
### 概要
- 高速化のための基本的なアプローチ
- ISUCONの事例を活用した改善方法

### 自社サービスの状況
- 低速なクエリやリクエストの特定
- CPU使用率、メモリ使用率の分析
  - あまりできていない
- 主要なボトルネックとなっている処理は何か？
  - パフォーマンスの指標を設定していないので定量的に示せない

### 改善点・対応方針
- `rails s` で起動しているためシングルスレッドになっているためマルチスレッド化する
  - k6などを利用し並列処理時のパフォーマンスを計測する
  - 他に暇しているCPUがないかを調査する
- mysqlなどのプロセスがCPUを占有し、railsが本来のパフォーマンスを発揮できていないといったことはないか調査

---

## 4章 シナリオを持った負荷試験: ×
### 概要
- 負荷試験の必要性とツールの活用方法(`k6`)
  - RspecのCapybaraのようにユーザーによる一連の操作を模倣し、その効果測定を行える
- 闇雲にサーバー増強してもボトルネックを解消できないことには限界がある
- 実際のアクセスパターンを模した負荷試験の設計

### 自社サービスの状況
- 負荷試験を実施しているか？
  - していない
- 使用しているツールは何か？（alp, abなど）
  - 利用していない

### 改善点・対応方針
- abコマンドなどを利用して負荷試験を行えれば良いのだが本番環境では行えないし、テスト環境はスペックが違うから単縦な比較ができなかったりとなかなか腰が重いのでやらないかもしれない（やるとしたらNew Relicでわかるボトルネックを全て潰した後かなCapybaraで似たようなことが出来ればいいのだけど） 

```bash
# abコマンドサンプル
$ apt update
$ apt install apache2-utils
$ ab -c 1 -n 10 http://localhost/ # 一並列で十回リクエストする
```

---

## 5章 データベースのパフォーマンス: △
### 概要
- MySQLのパフォーマンスチューニング
  - `SHOW PROCESSLIST;`で実行中のプロセスの一覧を取得できる
- クエリ最適化の基本
  - 

### 自社サービスの状況
- クエリの実行速度を計測しているか？
  - new relicで確認できる
- インデックスの最適化はされているか？
  - ある程度やったがまだありそう
- スロークエリログを活用しているか？
  - new relicで確認できる
![スクリーンショット_2025-03-12_21_33_54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427079/a497d8e2-1c94-49f7-a13d-126ce2474039.png)
### 改善点・対応方針
- 重いクエリがあったら`EXPLAIN`してみる
  - possible_keysがあるか(NULLだとインデックスが効いていない)
  - 大量のrowをスキャンしていないか
  - extraに`Using temporary`が表示されていないか
    - 一時テーブルを作成するくらい重い処理である可能性
  - extraに`Using filesort`が表示されていないか
    - 並び替えするカラムにインデックスがない可能性
  - extraに`Backwards index scan`が表示されていないか
    - 昇順インデックスを逆から参照するという非効率な処理である可能性
- インデックスの見直し
  - 並び替えするカラムにインデックスがなかったりしないか
  - 昇順インデックスを使った方がいい箇所はないか
  - 全文検索インデックスを付与した方がいい箇所はないか
    - LIKEによる検索でもっと最適化できる箇所があるかもしれない
  - 空間インデックスは該当箇所がないと思われるためスキップ
  - FORCE INDEXの利用検討
    - ただ該当箇所の調査が大変そう
  - STRAIGHT_JOINの利用検討
    - ただ該当箇所の調査やプログラム改修が大変そう
- クエリキャッシュの適用
  - 毎回ヘッダーとかで参照しているクエリはキャッシュ化できたらよさそう
- データベースの分割・最適化
  - MySQL8+だともっと色々最適化できそう

---

## 6章 リバースプロキシ: △
### 概要
- nginxを活用したパフォーマンス向上
- ロードバランシングとキャッシュ戦略

### 自社サービスの状況
- nginxの設定は適切か？
  - AWSのロードバランサを利用している
- 静的コンテンツのキャッシュ戦略を適用しているか？
  - おそらくしていない

### 改善点・対応方針
- Javascript CSS 画像をロードバランサに配置できたりしないか調査

nginxを利用していないためこの章の内容から得られるものは少なかった

---

## 7章: キャッシュ戦略: ×
### 概要
- キャッシュの適切な活用法
- 過剰なキャッシュによる問題点

### 自社サービスの状況
- CDNやアプリケーションキャッシュを活用しているか？
  - ElasticCache?

### 改善点・対応方針
- ElasticCacheの設定を確認してみる
- ただ本格的にキャッシュを利用する際には以下に注意
  - データの不整合を起こさないか
  - 誤ってキャッシュされたデータが残り続けてしまわないか
  - キャッシュがどれくらい残り続けるかの調整
  - キャッシュがどれくらい効果をなしているか

と、なかなか大変そう

---

## 8章 押さえておきたい高速化手法: ○
### 概要
- アプリケーションから外部コマンドを呼び出すデメリット
- デバッグモードで冗長なログを出力しない
- HTTPクライアントの適切な扱い方
- 静的ファイル配信をする際の注意点
- Cache-Controlヘッダーの活用方法

### 自社サービスの状況
- アプリケーションから外部コマンドを呼び出してはいない
- 静的ファイルの配信は最適化されていない（というかボトルネックになっていない）
- Cache-Controlヘッダーは不明 

### 改善点・対応方針
- ログレベルを念の為調査
- S3やCloudFrontで静的ファイル配信などできることがないかざっと調べてみる
- Cache-Controlヘッダーの利用状況や利用できる箇所がないか調査

---

## 9章 OSのチューニング: ×
### 概要
- OSレイヤーでのパフォーマンス最適化
- ファイルシステム、ネットワーク設定の改善

### 自社サービスの状況
- OSの設定をチューニングしているか？
  - していないがAWSがよしなにそこそこやってくれていると思われる
- ファイルディスクリプタの上限は適切か？
  - 要確認
- TCP設定の最適化は行っているか？
  - nginx使っていないのでやれることは少ない（と思われる）

### 改善点・対応方針
- AWSの設定を確認してみる
  - ulimitの Max open filesを確認してみる(mysqldやpuma sidekiq)
```bash
$ ps axufww | grep mysql
# => 9999
$ cat /proc/9999/limits
```
- net.core.somaxconn(ソケット通信における同時最大接続数)

```bash
$ sysctl net.core.somaxconn
```

- net.ipv4.ip_local_port_range(アプリケーションサーバーがDB接続などに利用するエフェメラルポート)

※22, 80 443など聞き馴染みのあるポートはシステムポート(1~1023)といい、それ以降の動的に割り当てられるポートをエフェメラルポートという

```bash
$ sysctl net.ipv4.ip_local_port_range
```

- UNIX domain socket
これはAPサーバーから同じwebサーバー内のアプリケーションへの接続の際の最適化なのであまり使えないかもしれない

- MTU(ネットワークインターフェイスから送信できる最大サイズ)
```bash
$ ip link show enp0s3
```
---

## まとめ
- CPUが高い低いだけでなく、どのプロセスがどれくらい占有しているかやスループットとレイテンシの関係について解像度が上がった(New relicをもっと理解できそう)
- インデックスについて理解度が上がった(今ままでは検索に使ってる箇所にインデックスをはればいいとしか思ってなかった)

---

参考文献

https://zenn.dev/siva_dev/articles/ac7512ba53c947

https://qiita.com/MarthaS/items/c136a4f6d58845a7fa1e