インフラエンジニアの羅針盤

インフラエンジニア1〜3年目のための技術ガイド

PostgreSQL 18 アーキテクチャ入門:プロセス構成とメモリの仕組み

公開
現場で迷わない PostgreSQL 18 構築・運用ガイド 第1回 アイキャッチ:アーキテクチャ入門(プロセス構成とメモリの仕組み)



PostgreSQLをLinuxサーバー上に構築し、安定して運用するためには、データベース内部でどのようなプロセスが起動し、メモリがどのように消費されているかを正確に把握しておく必要があります。パラメータの調整やパフォーマンス低下への対応、障害発生時の切り分けは、すべてアーキテクチャの理解が前提となります。

本記事の到達目標

  • クライアント接続からクエリ実行に至るプロセス分岐(fork)モデルの構造を説明できる。
  • CheckpointerやWAL Writerなど、主要なバックグラウンドプロセスの責務を整理できる。
  • 共有メモリ(shared_buffers等)とローカルメモリ(work_mem等)の違いを理解し、メモリサイジングの基本原則を判断できる。

検証環境情報

  • ホストOS:AlmaLinux 10.2 (Lavender Lion) x86_64
  • カーネル:Linux 6.12.0
  • データベース:PostgreSQL 18.6 (PGDG公式RPM版)
  • 検証ホスト名:alma-db01 (IP: 192.168.2.132)
目次
  1. 1. PostgreSQLの全体像とプロセス駆動モデル
    1. 1.1 プロセス分岐(fork)モデルの特徴とマルチスレッドとの違い
    2. 1.2 クライアント接続フローとバックエンドプロセスの生成
  2. 2. プロセス構成の整理:各プロセスの責務
    1. 2.1 主プロセス(Postmaster)とバックエンドプロセス
    2. 2.2 主要バックグラウンドプロセス一覧
  3. 3. メモリ構造の二大領域:共有メモリ vs ローカルメモリ
    1. 3.1 共有メモリ領域(Shared Memory Area)の役割
    2. 3.2 ローカルメモリ領域(Local Memory Area)とメモリ枯渇のメカニズム
    3. 3.3 LinuxカーネルパラメータとOOM Killer対策(overcommit設計)
    4. 3.4 Huge Pages(ヒュージページ)の採用メリットと設定実務
  4. 4. 実機で確認するPostgreSQLアーキテクチャ(AlmaLinux 10)
    1. 4.1 psコマンドによるプロセスツリーの確認
    2. 4.2 内部ビュー pg_stat_activity とパラメータの照合
    3. 4.3 work_mem不足によるディスク一時ファイル(Temporary File)の出力検証
  5. 5. データの整合性を保つ仕組み:WALとチェックポイント
    1. 5.1 なぜWAL(Write-Ahead Logging)が必要なのか
    2. 5.2 チェックポイントの動作シーケンス
  6. 6. 実務トラブルシューティング事例集(3大障害シナリオと解決策)
    1. 6.1 シナリオ1:OOM Killer発動によるPostgreSQL強制終了とクラッシュリカバリ
    2. 6.2 シナリオ2:共有メモリ(shared_buffers)過大設定によるサービス起動失敗
    3. 6.3 シナリオ3:チェックポイントスパイク(急激なディスクI/O集中)と平滑化チューニング
  7. 7. 実務チートシート:メモリサイジング計算式とリアルタイム診断クエリ集
    1. 7.1 本番環境メモリサイジング黄金比計算シート
    2. 7.2 プロセス・メモリ状態のリアルタイム診断クエリ一覧
  8. 8. 理解度確認演習問題(全2問・解答解説付き)
  9. 9. まとめと次回予告

1. PostgreSQLの全体像とプロセス駆動モデル

1.1 プロセス分岐(fork)モデルの特徴とマルチスレッドとの違い

リレーショナルデータベースのアーキテクチャには、マルチスレッドモデルを採用するもの(MySQLやSQL Serverなど)と、マルチプロセスモデルを採用するものがあります。PostgreSQLは伝統的にマルチプロセスモデル(プロセス駆動型アーキテクチャ)を一貫して採用しています。

マルチプロセスモデルでは、データベースの起動時にまず単一の親プロセス(Postmaster)が立ち上がります。外部からクライアントの接続要求が届くたびに、OSのシステムコールであるfork()を発行し、その接続専用の独立した子プロセス(バックエンドプロセス)を生成します。

この設計には以下の特徴があります。

  • 堅牢性とメモリ保護:各バックエンドプロセスはOSによって独立した仮想アドレス空間が割り当てられます。仮に特定のセッションで重篤なエラーやメモリアクセス違反が発生しても、その影響はそのプロセス内にとどまり、データベース全体がクラッシュする事態を防止できます。
  • コンテキストスイッチの負荷:スレッドと異なり、プロセス切り替えにはOSレベルの重いコンテキストスイッチを伴います。そのため、数千件規模の同時接続を直接受け付ける運用には向いておらず、コネクションプーラー(PgBouncer等)の併用が前提となります。

1.2 クライアント接続フローとバックエンドプロセスの生成

クライアント(Webアプリケーションやpsqlコマンド等)がPostgreSQLに接続を開始してからクエリを実行するまでの流れは以下の順序で進行します。

  1. ポート監視:親プロセスが指定されたポート(標準はTCP 5432)で接続要求を待ち受けます。
  2. 認証処理:接続要求を受信すると、pg_hba.confに定義された認証規則に基づいてクライアントのIPアドレス、ユーザー名、パスワード(SCRAM-SHA-256等)を検証します。
  3. プロセスの分岐(fork):認証が成立すると、親プロセスが自身を複製して新しいバックエンドプロセスを起動し、ソケットの制御権を渡します。
  4. クエリの実行:生成されたバックエンドプロセスは、クライアントからのSQL文を受信し、構文解析(パーサー)、書き換え(リライタ)、実行計画の策定(オプティマイザ)、そして実行エンジンでのデータ取得を担当します。
PostgreSQL プロセス&メモリ全体アーキテクチャ(主プロセス・バックエンド・共有メモリ・バックグラウンドプロセスの相関図)
図1: PostgreSQL プロセス&メモリ全体アーキテクチャ

2. プロセス構成の整理:各プロセスの責務

2.1 主プロセス(Postmaster)とバックエンドプロセス

PostgreSQLを構成するプロセス群は、大きく「主プロセス」「バックエンドプロセス」「バックグラウンドプロセス」の3系統に分類されます。

  • 主プロセス(Postmaster):システム起動時にPID 1相当として動作する統括プロセスです。共有メモリ領域を初期化して確保し、常駐バックグラウンドプロセス群を起動します。その後はポート5432の監視と、異常終了した子プロセスの検知・再起動を担当します。実行バイナリ名はpostgresです。
  • バックエンドプロセス(Backend Process):クライアント接続ごとに1対1で対応する作業プロセスです。クライアントから送られたSQL文を処理し、結果セットを返却します。セッションが切断されると、このプロセスは役目を終えて終了します。

2.2 主要バックグラウンドプロセス一覧

PostgreSQLがデータの整合性や高速な書き込み、自動メンテナンスを維持するために、Postmasterの配下で常駐動作している補助プロセス群が存在します。

プロセス名 プロセス表示名 主な役割と機能
Checkpointer postgres: checkpointer 定期的に共有メモリ内のダーティページ(更新済みブロック)を一括スキャンし、データファイル(ディスク)へ同期フラッシュします。
Background Writer postgres: background writer 共有バッファ内の使用済みページをディスクへ先行して書き出し、バックエンドプロセスが新しいデータを読み込むための空き領域を常に確保します。
WAL Writer postgres: walwriter WALバッファ内に蓄積された更新履歴ログを、定期的にディスク上のWALセグメントファイルへフラッシュします。
Autovacuum Launcher postgres: autovacuum launcher テーブルの更新・削除頻度を監視し、不要領域を回収するAutovacuum Workerプロセスを必要に応じて起動・管理します。
Logger postgres: logger 各プロセスが出力するエラーメッセージやクエリログを捕捉し、指定されたログファイルへ安全に書き込みます(設定により有効化)。
Logical Replication Launcher postgres: logical replication launcher 論理レプリケーションのサブスクリプションを監視し、同期用のワーカースレッドを立ち上げます。

重要ポイント:Checkpointer と Background Writer の違い

どちらも「共有メモリのデータをディスクに書く」プロセスですが、目的が異なります。Checkpointerは「障害復旧の基点(チェックポイント)を作るため」に全ダーティページを同期します。一方、Background Writerは「クエリ処理中のバックエンドプロセスがバッファ不足で待たされるのを防ぐため」に、少しずつ空きバッファを先行確保します。

3. メモリ構造の二大領域:共有メモリ vs ローカルメモリ

PostgreSQLのメモリ空間は、すべてのプロセスから参照可能な共有メモリ領域(Shared Memory Area)と、個別のバックエンドプロセスのみが利用するローカルメモリ領域(Local Memory Area)に二分されます。この構造の違いを把握することが、サーバーダウンを防ぐサイジングの第一歩となります。

3.1 共有メモリ領域(Shared Memory Area)の役割

共有メモリは、Postmasterプロセスの起動時にOSからまとめて割り当てられる領域です。すべてのバックエンドプロセスおよび補助プロセスが共通してアクセスします。

  • データバッファ(shared_buffers):テーブルやインデックスの実データブロック(標準8KB)をメモリ上にキャッシュしておく最重要領域です。読み取り要求があった場合、まずこの領域を検索し、ヒットすればディスクI/Oを発生させずに高速返却します。一般にサーバー搭載物理RAMの25パーセント程度を目安に設定します。
  • WALバッファ(wal_buffers):トランザクションの変更履歴ログをディスクに書き出す前に一時保管するバッファです。コミット時またはバッファ満杯時にディスクへフラッシュされます。
  • ロック管理・プロセス間通信領域:テーブルロックや行ロックの情報を保持するLock Table、コミット状態を管理するCLOG(pg_xact)などが配置されます。

3.2 ローカルメモリ領域(Local Memory Area)とメモリ枯渇のメカニズム

ローカルメモリは、各バックエンドプロセスが必要に応じてオンデマンドでOSから確保する一時的なメモリ領域です。接続が終了するか、クエリの特定工程が完了すると解放されます。

  • work_mem:ORDER BYによるソート処理や、ハッシュ結合(Hash Join)、ビットマップ走査などの実行時に使用されます。初期値は通常4MB程度です。
  • maintenance_work_mem:VACUUMCREATE INDEX、外部キー制約の追加などのメンテナンス作業専用のメモリです。日常クエリ用のwork_memよりも大きめの値(64MB〜1GB程度)を設定します。
  • temp_buffers:セッション内で作成された一時テーブル(TEMPORARY TABLE)のデータブロックをキャッシュする領域です。

運用上の注意点:work_memによるOOM Killerの危険性

work_memは「1接続あたり」のメモリ上限ではありません。「1つのクエリ内のソートやハッシュノードごと」に消費されます。複雑なクエリで複数のソートとハッシュ結合が同時に走ると、1セッションだけでwork_memの数倍のメモリが確保されます。接続数が増大した際に物理メモリを使い果たし、LinuxカーネルのOOM KillerによってPostgreSQLのプロセスが強制終了されるトラブルの多くは、このwork_memの過剰設定が原因です。

3.3 LinuxカーネルパラメータとOOM Killer対策(overcommit設計)

Linuxカーネルのメモリ管理には、物理メモリ以上の仮想メモリをプロセスへ楽観的に割り当てる「メモリオーバーコミット(Overcommit)」機能が存在します。デフォルト(vm.overcommit_memory = 0)では、実際に書き込みが発生して物理メモリが完全に枯渇した瞬間に、カーネルが最もメモリを消費しているプロセスを「OOM Killer」によって強制終了(SIGKILL)します。

データベースサーバーにおいて、Postmaster親プロセスやバックエンドプロセスがOOM Killerに殺されると、PostgreSQLはデータ不整合を防ぐために全接続を強制遮断して緊急クラッシュリカバリモード(REDOログの再適用)に突入し、数分から数十分のサービス全停止を引き起こします。

エンタープライズDBにおけるOSメモリ推奨設計

  • vm.overcommit_memory = 2オーバーコミットを厳格に無効化し、メモリ確保要求が物理上限を超えた時点で即座にエラーを返却させます(プロセス強制KILLを防止)。
  • vm.overcommit_ratio = 80コミット可能な仮想メモリ総量を「スワップ容量 + 物理メモリ × 80%」に制限します。
# /etc/sysctl.d/99-postgresql.conf への推奨追記設定
vm.overcommit_memory = 2
vm.overcommit_ratio = 80

# 設定の即時反映
$ sudo sysctl -p /etc/sysctl.d/99-postgresql.conf

3.4 Huge Pages(ヒュージページ)の採用メリットと設定実務

Linuxの標準メモリアーキテクチャでは、メモリは「4KB」単位のページで管理されます。しかし、shared_buffers に8GBや32GBといった大容量メモリを割り当てた場合、CPU内部のページテーブル(TLB: Translation Lookaside Buffer)のエントリ数が膨大になり、TLBミスヒットによるCPUオーバーヘッドや、ページテーブル自体による数十MB〜数百MBのメモリ無駄消費が発生します。

Huge Pages(2MB単位の巨大ページ) を有効化することで、ページ管理エントリ数を1/512に削減し、CPUのメモリアクセス性能を大幅に改善できます。

# 1. 必要なヒュージページ数の算出(例:shared_buffersが1GBの場合、約550ページ)
$ grep -i hugepage /proc/meminfo

# 2. カーネルパラメータでヒュージページを常時予約
# /etc/sysctl.d/99-hugepages.conf
vm.nr_hugepages = 600

# 3. postgresql.conf での有効化
huge_pages = on  # 'try'(試行)ではなく 'on' で明示確保を強制

huge_pages = on を設定しておくと、PostgreSQL起動時にヒュージページが十分に確保できない場合に起動エラーとなるため、意図せず4KBページで稼働してしまう「サイレントな性能低下」を確実に防止できます。

4. 実機で確認するPostgreSQLアーキテクチャ(AlmaLinux 10)

理論として整理したプロセスツリーとメモリ設定が、実際のAlmaLinux 10環境上でどのように表現されているかをコマンドを用いて実機確認します。

4.1 psコマンドによるプロセスツリーの確認

システム上でPostgreSQLが稼働している状態で、psコマンドに--forestオプションを付与して実行すると、Postmaster親プロセスと子プロセスの親子関係が視覚的に確認できます。

実行手順

  1. 対象ノード(alma-db01)にログインします。
  2. ps -ef --forest | grep -E 'postgres|UID' を実行します。
$ ps -ef --forest | grep -E 'postgres|UID'
UID          PID    PPID  C STIME TTY          TIME CMD
postgres    2571       1  0 00:44 ?        00:00:00 /usr/pgsql-18/bin/postgres -D /var/lib/pgsql/18/data
postgres    2572    2571  0 00:44 ?        00:00:00  \_ postgres: logger 
postgres    2573    2571  0 00:44 ?        00:00:00  \_ postgres: checkpointer 
postgres    2574    2571  0 00:44 ?        00:00:00  \_ postgres: background writer 
postgres    2576    2571  0 00:44 ?        00:00:00  \_ postgres: walwriter 
postgres    2577    2571  0 00:44 ?        00:00:00  \_ postgres: autovacuum launcher 
postgres    2578    2571  0 00:44 ?        00:00:00  \_ postgres: logical replication launcher 

出力ログから以下の事実が確認できます。

  • 親プロセス(PID 2571)はPPIDが1(systemd)であり、/usr/pgsql-18/bin/postgres -D /var/lib/pgsql/18/dataとして起動しています。
  • その配下に、PPID 2571を持つ子プロセスとしてloggercheckpointerbackground writerwalwriterautovacuum launcherlogical replication launcherが階層状に紐付いています。

4.2 内部ビュー pg_stat_activity とパラメータの照合

次に、psqlインターフェースから内部ビューpg_stat_activityを検索し、PostgreSQL自身が認識しているプロセス一覧と主要メモリパラメータを取得します。

$ sudo -u postgres /usr/pgsql-18/bin/psql

-- メモリパラメータの現在値照合
SHOW shared_buffers;
--  shared_buffers 
-- ----------------
--  128MB

SHOW work_mem;
--  work_mem 
-- ----------
--  4MB

SHOW maintenance_work_mem;
--  maintenance_work_mem 
-- ----------------------
--  64MB

-- 稼働中のバックエンド種別の確認
SELECT pid, backend_type FROM pg_stat_activity ORDER BY pid;
/*
 pid  |         backend_type         
------+------------------------------
 2573 | checkpointer
 2574 | background writer
 2576 | walwriter
 2577 | autovacuum launcher
 2578 | logical replication launcher
 2642 | client backend
(6 行)
*/

PID 2642として表示されているclient backendが、現在psqlを実行しているクライアント接続専用のバックエンドプロセスです。OSのpsコマンドの出力と内部ビューの情報が正確に一致していることが実証されます。

4.3 work_mem不足によるディスク一時ファイル(Temporary File)の出力検証

各バックエンドプロセスがクエリを実行する際、work_memが不足すると何が起きるかを実機でシミュレーションします。

PostgreSQLでは、ソートやハッシュ結合に必要なデータ量が work_mem の上限を超過した場合、メモリ内ソート(quicksort)を諦め、ディスク上の pgsql_tmp/ ディレクトリに一時ファイル(Temporary File)を書き出して外部マージソート(External Merge Sort)を実行します。ディスクI/Oが発生するため、クエリ応答速度は数十倍から数百倍に急落します。

実行手順

  1. 一時ファイルの生成をログへ記録するため、セッション内で SET log_temp_files = 0; を設定します(0バイト以上の一時ファイルを全記録)。
  2. 意図的にソートメモリを圧迫するため、SET work_mem = '64kB'; と極小値に設定します。
  3. 10万件の連番データを生成し、ORDER BY でソートするクエリを EXPLAIN ANALYZE 付きで実行します。
-- セッションパラメータの変更
bench_db=# SET log_temp_files = 0;
bench_db=# SET work_mem = '64kB';

-- ソート実行計画の採取
bench_db=# EXPLAIN ANALYZE SELECT generate_series(1, 100000) ORDER BY 1 DESC;
                                                    QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Sort  (cost=12543.00..12793.00 rows=100000 width=4) (actual time=24.112..31.845 rows=100000 loops=1)
   Sort Key: (generate_series(1, 100000)) DESC
   Sort Method: external merge  Disk: 1768kB
   ->  ProjectSet  (cost=0.00..5002.00 rows=100000 width=4) (actual time=0.004..6.211 rows=100000 loops=1)
         ->  Result  (cost=0.00..0.01 rows=1 width=0) (actual time=0.001..0.001 rows=1 loops=1)
 Planning Time: 0.045 ms
 Execution Time: 35.120 ms
(7 行)

実行計画の Sort Method に着目してください。external merge Disk: 1768kB と表示されており、メモリに収まらず約1.8MBのディスク書き込みが発生したことが分かります。サーバーログ(postgresql-*.log)にも以下のように明確に出力されます。

2026-09-09 20:45:12.418 JST [2642] LOG:  一時ファイル: パス"base/pgsql_tmp/pgsql_tmp2642.0", サイズ 1810432 バイト
2026-09-09 20:45:12.419 JST [2642] STATEMENT:  EXPLAIN ANALYZE SELECT generate_series(1, 100000) ORDER BY 1 DESC;

本番環境では、log_temp_files を適切な閾値(例:10MB)に設定し、一時ファイルを大量消費している重いクエリを検知して work_mem の見直しやインデックス付与(ソート回避)を行うのが運用チューニングの鉄則です。

5. データの整合性を保つ仕組み:WALとチェックポイント

5.1 なぜWAL(Write-Ahead Logging)が必要なのか

リレーショナルデータベースにおいて、トランザクションのコミット要求があるたびにディスク上のデータファイル(テーブルやインデックスの実体)へ直接書き込みを行うと、ディスクヘッドのランダムアクセスが頻発してスループットが極端に低下します。

そこでPostgreSQLは、WAL(Write-Ahead Logging: 先行書き込みログ)という仕組みを採用しています。更新処理が発生した際、データ本体をディスクに直接書き込むのではなく、まず以下の手順を踏みます。

  1. 共有メモリ上のバッファページを更新します(この状態のページをダーティページと呼びます)。
  2. 同時に、どのような変更を加えたかという差分履歴(REDOログレコード)をWALバッファに追記します。
  3. クライアントからCOMMITが発行されると、WALバッファにたまっているログレコードのみをディスク上のWALファイル(pg_wal/ディレクトリ配下)へシーケンシャル(連続)書き込みし、fsyncを発行して物理フラッシュします。
  4. WALの物理フラッシュが完了した時点で、クライアントへ「コミット成功」を通知します。

シーケンシャルアクセスはランダムアクセスに比べて極めて高速であるため、高いトランザクション性能を維持しながら、ACID特性の耐久性(Durability)を保証できます。万一この直後にサーバーの電源が落ちても、再起動時にWALログを読み出してダーティページを再現(REDO)できるため、コミット済みデータが失われることはありません。

トランザクションコミットとディスク書き込みフロー(WAL&チェックポイントの同期シーケンス)
図2: トランザクションコミットとディスク書き込みフロー

5.2 チェックポイントの動作シーケンス

WALファイルに追記を続けるだけでは、ディスク容量がいずれ枯渇し、障害発生時のクラッシュリカバリ時間も長大化してしまいます。そこで、メモリ上に滞留しているダーティページを実際のデータファイルへまとめて反映させる定期処理がチェックポイント(Checkpoint)です。

チェックポイントの処理シーケンスは以下の通りです。

  1. 契機の検知:設定された時間(checkpoint_timeout、標準5分)が経過するか、書き込まれたWALログの総量が上限(max_wal_size、標準1GB)に達すると、Checkpointerプロセスが動作を開始します。
  2. ディスク書き出し:共有バッファ内の全ダーティページをディスク上のデータファイル(base/ディレクトリ配下)へフラッシュし、fsyncを実行します。
  3. 制御ファイルの更新:クラスタの制御情報ファイル(global/pg_control)に、「このチェックポイント位置(LSN: ログシーケンス番号)までデータファイルへの同期が完了した」という情報を記録します。
  4. 古いWALの再利用:チェックポイント完了地点より過去の古いWALセグメントはリカバリに不要となるため、削除または次回の書き込み用としてリサイクル(名前変更)されます。
# pg_controldata による制御情報の確認
$ sudo -u postgres pg_controldata /var/lib/pgsql/data | grep -E 'チェックポイント|状態'
データベースクラスタの状態:                  運用中
最終チェックポイント位置:                    0/14EFA28
最終チェックポイントのREDO位置:              0/14EFA28
最終チェックポイントのREDO WALファイル:      000000010000000000000001
最終チェックポイント時刻:                    2026年09月09日 00時44分24秒

pg_controldataコマンドで確認できる「最終チェックポイントのREDO位置」が、万一障害が起きた際にロールフォワード(リカバリ)を開始すべき基点を示しています。

6. 実務トラブルシューティング事例集(3大障害シナリオと解決策)

アーキテクチャの基本構造を理解したところで、実務運用において頻発する代表的なメモリ・プロセス関連の障害シナリオ3選と、その原因特定手順および恒久対策を整理します。

6.1 シナリオ1:OOM Killer発動によるPostgreSQL強制終了とクラッシュリカバリ

物理メモリの枯渇により、LinuxカーネルのOOM(Out of Memory)KillerがPostgreSQLのバックエンドプロセスを強制終了(SIGKILL)させる障害です。1つのバックエンドプロセスがSIGKILLで停止すると、共有メモリ領域が不整合状態に陥る可能性があるため、主プロセス(Postmaster)はデータ保護のために他の全バックエンドを直ちに強制終了し、自動的にクラッシュリカバリモードへ遷移します。

OOM Killer発生時のPostgreSQLログ記録例

2026-09-09 21:15:30 JST [12345] LOG:  server process (PID 12890) was terminated by signal 9: Killed
2026-09-09 21:15:30 JST [12345] DETAIL:  Failed process was running: SELECT * FROM large_table ORDER BY payload;
2026-09-09 21:15:30 JST [12345] LOG:  terminating any other active server processes
2026-09-09 21:15:30 JST [12345] LOG:  all server processes terminated; reinitializing
2026-09-09 21:15:32 JST [13001] LOG:  database system was not properly shut down; automatic recovery in progress
2026-09-09 21:15:32 JST [13001] LOG:  redo starts at 0/1A2B3C4
2026-09-09 21:15:34 JST [13001] LOG:  redo done at 0/1A2D000 system usage: CPU: user: 0.05 s, system: 0.02 s, elapsed: 2.10 s
2026-09-09 21:15:34 JST [12345] LOG:  database system is ready to accept connections

原因:work_mem × 同時複雑クエリ数の掛け算により、OSの空き物理メモリ+スワップ領域を超過したことが主因です。また、Linuxのデフォルト挙動であるメモリのオーバーコミット(物理メモリ以上の仮想メモリ割り当てを許可する仕様)が拍車をかけます。

恒久対策手順

  • work_memの見直し:グローバル設定のwork_memを安全な値(16MB〜64MB程度)に抑え、バッチ処理等で大規模ソートを要する特定セッションのみSET work_mem = '512MB';を個別適用する運用に変更します。
  • Linuxオーバーコミット制御:/etc/sysctl.confにてvm.overcommit_memory = 2およびvm.overcommit_ratio = 80を設定し、実物理メモリとスワップ容量を超える過剰な仮想メモリ割り当てをOSレベルで拒否します。
  • OOM Killer対象からの除外(Postmaster保護):systemdユニット設定(/etc/systemd/system/postgresql-18.service.d/oom.conf等)においてOOMScoreAdjust=-1000を指定し、主プロセスが最優先で殺される事態を抑制します。

6.2 シナリオ2:共有メモリ(shared_buffers)過大設定によるサービス起動失敗

サーバー増強時などにshared_buffersを過剰に大きな値(物理メモリと同等以上、あるいはOSの許容共有メモリ上限以上)に設定してしまい、PostgreSQLサービスが起動できなくなる障害です。

$ sudo systemctl start postgresql-18
Job for postgresql-18.service failed because the control process exited with error code.

$ sudo journalctl -u postgresql-18 -n 20 --no-pager
Sep 09 21:20:10 alma-db01 postmaster[14100]: 2026-09-09 21:20:10 JST [14100] FATAL:  could not map anonymous shared memory: Cannot allocate memory
Sep 09 21:20:10 alma-db01 postmaster[14100]: 2026-09-09 21:20:10 JST [14100] HINT:  This error usually means that PostgreSQL's request for a shared memory segment exceeded available memory, swap space, or huge pages.
Sep 09 21:20:10 alma-db01 postmaster[14100]: 2026-09-09 21:20:10 JST [14100] LOG:  database system is shut down

原因:shared_buffersはPostgreSQLの起動時に一括してOSから匿名共有メモリ(mmap)として確保されます。搭載メモリ8GBのサーバーに対して誤ってshared_buffers = 16GBと指定した場合や、Huge Pages(ヒュージページ)の設定不足によりメモリマッピングが拒否された場合に発生します。

対策:実物理メモリの25%(8GB環境なら2GB、16GB環境なら4GB)を上限の目安としてpostgresql.confを修正し、再起動します。巨大なメモリ環境で25%超を割り当てる場合は、OSのHuge Pages(vm.nr_hugepages)を正しくサイジングした上で段階的に引き上げます。

6.3 シナリオ3:チェックポイントスパイク(急激なディスクI/O集中)と平滑化チューニング

大量の更新・挿入トランザクションが集中した際、突発的にディスクI/O使用率が100%に張り付き、Webアプリケーション側のレスポンスが数百ミリ秒から数秒に急悪化する現象です。通称「チェックポイントスパイク(Checkpoint Spike)」と呼ばれます。

チェックポイント多発時のログ(ログ設定 log_checkpoints = on の場合)

2026-09-09 21:30:00 JST [11000] LOG:  checkpoint starting: time
2026-09-09 21:30:15 JST [11000] LOG:  checkpoint complete: wrote 45200 buffers (27.6%); 0 WAL file(s) added, 12 removed, 8 recycled; write=14.210 s, sync=0.812 s, total=15.022 s; sync files=180, longest=0.320 s, average=0.004 s; distance=393216 kB, estimate=410000 kB
2026-09-09 21:31:05 JST [11000] LOG:  checkpoints are occurring too frequently (35 seconds apart)
2026-09-09 21:31:05 JST [11000] HINT:  Consider increasing the configuration parameter "max_wal_size".

原因と平滑化メカニズム:チェックポイントが発生すると、共有バッファ上の全ダーティページをストレージへフラッシュする必要があります。この書き出し処理を短時間で急激に行うと、ディスク帯域を使い果たしてバックエンドプロセスの通常読み書きを阻害します。

スパイク平滑化の3大パラメータ調整

  • checkpoint_completion_target = 0.9チェックポイント間隔(checkpoint_timeout)の90%の時間をかけてダーティページを分散してディスクへ書き出します(PostgreSQL 14以降はデフォルト0.9)。書き込み速度を平滑化し、突発的なI/O飽和を防止します。
  • max_wal_size = 16GB〜32GBWAL生成量起因による頻繁な強制チェックポイントの発生を防止します。デフォルト(1GB)のままだと、バッチ処理等で即座に上限へ達し、スパイクが連続発生します。
  • checkpoint_timeout = 15min〜30min時間契機のチェックポイント間隔を適度に広げ、I/O頻度を抑制します(ただし、長くしすぎるとクラッシュ時のリカバリ所要時間が延びるトレードオフがあります)。

7. 実務チートシート:メモリサイジング計算式とリアルタイム診断クエリ集

本番システムのインフラ構築および運用フェーズで即座に活用できる、メモリサイジングの黄金比計算式と、プロセス稼働状況を把握するための実務用診断クエリをまとめました。

7.1 本番環境メモリサイジング黄金比計算シート

サーバー搭載の物理RAM容量に応じた、推奨初期設定値の算出式です。DB専用サーバー(Webサーバー等と相乗りしない構成)を前提としています。

パラメータ名 推奨サイジング式 / 目安値 物理RAM 8GB環境 物理RAM 32GB環境 設計上の注意事項
shared_buffers 物理RAMの 20% 〜 25% 2GB 8GB OSのページキャッシュ(FS Cache)にもメモリを残すため、50%超には設定しない。
work_mem (物理RAM - shared_buffers) × 0.2 / 想定同時ソート数 / 2 16MB 32MB〜64MB 同時ソート多重度を35〜40接続と想定した場合に約16MB。max_connections(100)全接続の最悪ケースを見込む場合は約6MBと保守的に設定。
maintenance_work_mem 物理RAMの 5% 〜 10%(最大2GB程度) 512MB 2GB VACUUMやインデックス作成(CREATE INDEX)専用。同時実行数は少ないため大きめに確保。
wal_buffers shared_buffersの約 3%(通常は 16MB固定で十分) 16MB 16MB 1トランザクションで書き出されるWAL量に対応。通常-1(自動設定=16MB)で最適。
effective_cache_size 物理RAMの 50% 〜 75% 6GB 24GB オプティマイザのコスト見積もり用参照値(実際のメモリ確保は行われない)。

7.2 プロセス・メモリ状態のリアルタイム診断クエリ一覧

実務でDB性能低下や不審なリソース消費を検知した際、psqlから即座に実行できるトラブル診断クエリです。

1. 現在接続中のプロセス一覧と実行中クエリ・待機イベントの把握

SELECT
    pid,
    usename,
    client_addr,
    application_name,
    state,
    wait_event_type,
    wait_event,
    now() - query_start AS duration,
    query
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY duration DESC;

アイドル(接続待機)以外の全プロセスを表示します。実行時間が異常に長いクエリ(長時間トランザクション)や、ロック待機(wait_event_type = ‘Lock’)に陥っているPIDを即座に特定できます。

2. 共有バッファのキャッシュ使用状況(pg_buffercache拡張機能の利用例)

-- 共有バッファ内におけるテーブル別キャッシュ占有状況を集計
SELECT
    c.relname,
    pg_size_pretty(count(*) * 8192) as buffered_bytes,
    round(100.0 * count(*) / (SELECT setting FROM pg_settings WHERE name='shared_buffers')::integer, 2) AS buffer_percent
FROM pg_buffercache b
JOIN pg_class c ON b.relfilenode = pg_relation_filenode(c.oid)
JOIN pg_database d ON (b.reldatabase = d.oid AND d.datname = current_database())
GROUP BY c.relname
ORDER BY count(*) DESC
LIMIT 10;

どのテーブルが共有バッファの大半を消費しているかを可視化します。特定の巨大ログテーブルがキャッシュを食い潰している事象などの発見に役立ちます。

8. 理解度確認演習問題(全2問・解答解説付き)

確認演習問題 1

PostgreSQLのプロセスアーキテクチャに関する以下の記述のうち、誤っているものを1つ選択してください。

  1. Postmaster(主プロセス)はクライアントからの接続要求を受け付け、バックエンドプロセスをforkして接続を委譲する。
  2. Background Writerプロセスは、定期的にすべてのダーティページをデータファイルへ完全に同期フラッシュ(fsync)し、チェックポイントレコードを記録する。
  3. WAL Writerプロセスは、トランザクションコミット時に先行書き込みログ(WAL)をディスク上のWALセグメントファイルへ永続化する。
  4. Autovacuum Launcherプロセスは、更新や削除によって発生した不要タプル(デッドタプル)を回収するワーカプロセスを適宜起動する。
解答と解説を見る

正解: 2

解説:

選択肢2が誤りです。すべてのダーティページをデータファイルに同期フラッシュし、チェックポイントレコードを制御ファイルに記録する役割を担うのはCheckpointerプロセスです。

Background Writerプロセスの主たる責務は、バックエンドプロセスが新しいブロックを読み込む際に空きバッファ不足でブロックされる事態を防ぐため、共有バッファ内の使用済みページを先行して少しずつディスクに書き出すことです。

確認演習問題 2

以下の環境条件において、最悪ケース(すべての接続で同時に3つのソートノードを含むクエリが実行された場合)で消費され得るソート用ローカルメモリの最大総量を計算してください。

  • データベース設定値:work_mem = 16MB
  • 同時実行クライアント接続数:50接続
  • 各クエリに含まれるソート・ハッシュ操作数:3箇所
解答と解説を見る

正解: 2,400MB(約2.4GB)

解説:

work_memは接続単位の上限値ではなく、SQL実行計画内の演算ノード(SortノードやHashノード)ごとに割り当てられます。

計算式は以下の通りです。

16MB(work_mem) × 3(ノード数) × 50(接続数) = 2,400MB

このように、接続数やクエリの複雑度によっては、単純計算の数倍のメモリが瞬間的に確保されます。サイジング設計時は、max_connectionsの数だけでなく、アプリケーションが発行するクエリの特性を考慮してwork_memを設定する必要があります。

9. まとめと次回予告

  • PostgreSQLはプロセス駆動モデル(マルチプロセス)を採用しており、接続ごとに独立したバックエンドプロセスがforkされる。
  • 共有メモリ(shared_buffers等)は全プロセスで共有され、ローカルメモリ(work_mem等)は各プロセスの操作ごとに個別に消費される。
  • 更新データはまずメモリ上でダーティページ化され、WALバッファからディスクへの先行同期書き込み(fsync)によって耐久性を担保する。
  • ダーティページのデータファイルへの反映は、Checkpointerによる遅延書き込みによってまとめて行われ、I/O負荷を平滑化している。

次回は第2回「【AlmaLinux 10】AppStream vs PGDG公式RPM 比較検証と初期構築」を解説します。OS標準のリポジトリから導入する手順と、PostgreSQLコミュニティ公式リポジトリから導入する手順の決定的な違い、パス構造の相違点、そして将来のバージョンアップを見据えた選定基準を実機検証で整理します。

PostgreSQL 18 構築・運用ガイド

本番パラメータの精密設計を書籍で学ぶ

本記事ではプロセスとメモリの基本概念を解説しました。実務のプロダクション環境において、サーバーの搭載物理メモリ(8GB / 32GB / 128GB)やCPUコア数、ストレージ種別(NVMe SSD等)に応じた厳密なパラメータ算定計算式(shared_buffers, work_mem, checkpoint_completion_target 等の黄金比設計)および負荷ベンチマーク検証については、Kindle書籍版『現場で困らない PostgreSQL 本番運用大全 —— 4ノード実機で作る高可用性・チューニング・障害復旧』第2章にて完全収録しています。

前の記事
次の記事