パフォーマンスチューニング　メモリの設定について超簡単にまとめる

中規模→大規模アプリで DB レスポンスを安定させるには、メモリ周りのチューニングも必要らしい。

簡単そうに設定できる
**shared_buffers**
**work_mem**
**maintenance_work_mem**
の3つの設定値に絞って設定をまとめる。


前回までのパフォーマンスチューニング
パフォーマンスチューニング：インデックスを超簡単にまとめる
https://qiita.com/tetsuro-seki/items/926c78b41e89a7a1d118

パフォーマンスチューニング：パーティショニングを超簡単にまとめる
https://qiita.com/tetsuro-seki/items/c2e3cdea504d6938a7fe

---

## 1. shared_buffers
- **役割**  
  PostgreSQL が “テーブル・インデックス読み取り” に使うキャッシュ（共有バッファ）
- **効果**  
  キャッシュヒット率が上がるほど、ディスク I/O を抑えて SELECT／JOIN が速くなる
- **設定目安**  
  総システムメモリの **25～40%**  
  例：サーバー 32GB → `shared_buffers = 8GB`～`12GB`
- **設定例**  
  ```conf
  # postgresql.conf
  shared_buffers = 8GB
  ```
**postgresql.conf**は通常のプロジェクトディレクトリとは別物。
ファイル内容を読むには
1. DB サーバーにログイン
2. cat /etc/postgresql/16/main/postgresql.conf などで中身を確認する必要がある


### 共有バッファの補足

- PostgreSQL サーバー全体で**共通利用**するメモリ領域  
- テーブルやインデックスのデータを**ページ単位**（通常8KB）でキャッシュ  
- 各クライアント（バックエンドプロセス）はまずここを参照し、  
  キャッシュにあればディスク I/O （input/output）を避けて高速に読み取れる  
- OS 標準のファイルキャッシュとは別に管理され、  
  データベース専用のキャッシュヒット率を高める役割  
- `shared_buffers` の設定を大きくするとヒット率が上がる一方、  
  メモリ使用量が増えるためシステム全体とのバランスが重要になってくる


  ## 2. work_mem
- **役割**  
  **クエリ単位**でソート・ハッシュ結合など一時データ用に割り当てるメモリ
- **効果**  
  大規模な `ORDER BY` / `GROUP BY` / `DISTINCT` / `JOIN` が高速化  
  小さすぎるとディスクにスワップされ、逆に遅くなる
- **設定目安**  
  平均 **4MB～64MB**（同時接続数×設定値で合計メモリを見積もる）  
  例：`work_mem = 16MB`（100接続で約1.6GB）
- **設定例**  


```conf
  # postgresql.conf
  work_mem = 16MB
```
## スワップ（swap）とは

- **OS のメモリ管理機能**  
  - 実メモリ（RAM）が不足したとき、使っていないメモリ領域（ページ）を“ディスク上の領域”に退避させる仕組み  
  - 退避先は「スワップ領域（swap partition／swap file）」と呼ばれる

- **スワップが起きる理由**  
  1. プロセス（アプリ）が大量のメモリを要求  
  2. RAM が足りなくなる  
  3. OS が「しばらく使われていないデータ」をディスクに書き出し、RAM を解放  
  4. 必要になったときはまたディスクから読み込む

- **メリット・デメリット**  
  - メリット：  
    - メモリ不足時でもシステムがクラッシュせず動き続けられる  
  - デメリット：  
    - ディスク（特に HDD）は RAM より何十倍も遅い → **処理遅延が大きくなる**  
    - 頻繁にスワップが発生すると、アプリ全体が極端に重くなる（スラッシング）

- **データベース運用での注意**  
  - `work_mem` や `shared_buffers` を大きくしすぎるとシステムの空き RAM が減り、スワップが起こりやすくなる  
  - スワップはディスク I/O なので、DB 性能に致命的な影響を与えることもある  
  - 目安として **スワップ使用率はなるべくゼロ** に保つのがベスト
  

  
  ## 3. maintenance_work_mem
- **役割**  
  `VACUUM`・`CREATE INDEX`・`ALTER TABLE`などのメンテナンス処理時に使う作業領域
- **効果**  
  大量データのVACUUMやインデックス再作成が速くなる。通常クエリには影響しないため、大きめ設定でもOK
- **設定目安**  
  **256MB～2GB**（一度に走るメンテ処理の規模に合わせて調整）  
  例：`maintenance_work_mem = 512MB`
- **設定例**  

```conf
  # postgresql.conf
  maintenance_work_mem = 512MB
```

### メンテナンス処理時の「作業領域」とは何か

PostgreSQL では、バックグラウンド／手動で実行するテーブル保守処理に際して、一時的に使うメモリ領域を **maintenance_work_mem** で設定。

| 処理             | 概要                                              | 一時領域の使い方                                    |
|------------------|---------------------------------------------------|-----------------------------------------------------|
| **VACUUM**       | 古い XMIN/XMAX を超えた行（死行）を削除し、空き領域を回収 | テーブル内の行検査や再配置など、一時データのキャッシュ |
| **CREATE INDEX** | インデックスを新規作成                              | ソートやハッシュアルゴリズムでキーを整理・構築     |
| **ALTER TABLE**  | カラム追加／型変更などの構造変更                   | 新旧データの変換や一時置き場として使用              |
| **ANALYZE**      | テーブル統計情報の収集                             | 統計用サンプル行を読み込み、一時バッファに保持      |

- **なぜ大きいほど速い**  
  これらの処理は大量データを一時的に「ソート」「集計」「変換」するため、**多くのメモリを使えるほどディスクへのスワップや一時ファイルを減らせ**、高速化につながる。  
- **通常クエリへの影響は**  
  通常の SELECT/INSERT/UPDATE では使われず、**メンテナンス専用**。大きめに設定しても日常処理に影響しない


  ## メモリ設定で「領域を割り当てる」イメージ

- **shared_buffers**  
  - PostgreSQL 起動時に **あらかじめ割り当て** → プロセス共有メモリとして確保される  
      - `shared_buffers = 8GB` とすると、OS 上のメモリ 8GB が PostgreSQL 用キャッシュとして先に予約される  
      - その後はテーブル／インデックスのページをここに読み込んで使用する

- **work_mem**  
  - **クエリ実行時に必要に応じて割り当て** → クエリ単位・作業単位で一時領域を使う  
  - `work_mem = 16MB` とすると、1回のソートやハッシュ結合処理で最大 16MB までメモリを使える  
  - クエリが複数同時に走れば、その都度 “最大16MBずつ” 割り当てられる（使わなければ消費されない）

- **maintenance_work_mem**  
  - **メンテナンス処理時に必要に応じて割り当て**  
  - `maintenance_work_mem = 512MB` とすると、VACUUM や CREATE INDEX 処理が行う一時ファイル／メモリ操作で最大512MBを利用可能  
  - 通常クエリでは使われず、メンテナンス時のみ消費

---

### まとめ
1. **shared_buffers** は「起動時に一括で確保」される常駐キャッシュ領域  
2. **work_mem／maintenance_work_mem** は必要になったときに都度割り当てられる一時領域  
3. 合計消費量 =  
   - `shared_buffers`（固定）＋  
   - `work_mem × 同時ソート数` ＋  
   - `maintenance_work_mem × 同時メンテ数`  
4. 設定値が大きすぎると **OS スワップ** の原因になるため、システム全体のメモリ量と同時実行数を考慮して調整する

  ## 4. 設定手順まとめ

1.**設定ファイル編集**  

```bash
   vi /etc/postgresql/16/main/postgresql.conf
```

2.**各パラメータを調整**

```
shared_buffers = 8GB
work_mem = 16MB
maintenance_work_mem = 512MB
```

3.**PostgreSQL 再起動**

```
systemctl restart postgresql
```

4.*動作確認*
- SHOW shared_buffers;
- SHOW work_mem;
- SHOW maintenance_work_mem;
- EXPLAIN ANALYZE でクエリ実行時間を確認

正直今の開発ではメモリは考えてもいなかった。
設定するにはそれぞれの処理でどの程度のメモリを使っているかを確認する必要があることは分かった。
すべて対応しようとすると本当に時間が溶けていきそう。