中規模→大規模アプリで 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
-
設定例
# postgresql.conf shared_buffers = 8GB
postgresql.confは通常のプロジェクトディレクトリとは別物。
ファイル内容を読むには
- DB サーバーにログイン
- 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) -
設定例
# postgresql.conf
work_mem = 16MB
スワップ(swap)とは
-
OS のメモリ管理機能
- 実メモリ(RAM)が不足したとき、使っていないメモリ領域(ページ)を“ディスク上の領域”に退避させる仕組み
- 退避先は「スワップ領域(swap partition/swap file)」と呼ばれる
-
スワップが起きる理由
- プロセス(アプリ)が大量のメモリを要求
- RAM が足りなくなる
- OS が「しばらく使われていないデータ」をディスクに書き出し、RAM を解放
- 必要になったときはまたディスクから読み込む
-
メリット・デメリット
- メリット:
- メモリ不足時でもシステムがクラッシュせず動き続けられる
- デメリット:
- ディスク(特に 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
-
設定例
# 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 用キャッシュとして先に予約される - その後はテーブル/インデックスのページをここに読み込んで使用する
-
- PostgreSQL 起動時に あらかじめ割り当て → プロセス共有メモリとして確保される
-
work_mem
- クエリ実行時に必要に応じて割り当て → クエリ単位・作業単位で一時領域を使う
-
work_mem = 16MB
とすると、1回のソートやハッシュ結合処理で最大 16MB までメモリを使える - クエリが複数同時に走れば、その都度 “最大16MBずつ” 割り当てられる(使わなければ消費されない)
-
maintenance_work_mem
- メンテナンス処理時に必要に応じて割り当て
-
maintenance_work_mem = 512MB
とすると、VACUUM や CREATE INDEX 処理が行う一時ファイル/メモリ操作で最大512MBを利用可能 - 通常クエリでは使われず、メンテナンス時のみ消費
まとめ
- shared_buffers は「起動時に一括で確保」される常駐キャッシュ領域
- work_mem/maintenance_work_mem は必要になったときに都度割り当てられる一時領域
- 合計消費量 =
-
shared_buffers
(固定)+ -
work_mem × 同時ソート数
+ maintenance_work_mem × 同時メンテ数
-
- 設定値が大きすぎると OS スワップ の原因になるため、システム全体のメモリ量と同時実行数を考慮して調整する
4. 設定手順まとめ
1.設定ファイル編集
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 でクエリ実行時間を確認
正直今の開発ではメモリは考えてもいなかった。
設定するにはそれぞれの処理でどの程度のメモリを使っているかを確認する必要があることは分かった。
すべて対応しようとすると本当に時間が溶けていきそう。