データベース運用において、バックアップはシステム障害や人為的ミスからデータを守る最後の防衛線です。日常的にスクリプトを動かしてバックアップファイルを生成していても、「いざ本番障害が発生した際に、目的のデータのみを迅速かつ確実に復旧できるか」という問いに対して自信を持って答えられるエンジニアは決して多くありません。バックアップの真の価値は、保存することではなく、正確に復元できることにあります。
PostgreSQLが提供する論理バックアップツールpg_dumpと、その復元を担うpg_restoreは、小中規模のデータベース運用や環境移行において極めて強力な標準ユーティリティです。しかし、出力形式の選定ミスやオプション指定の誤解により、復元時に不要なエラーやダウンタイムの長期化を引き起こすケースが頻発しています。本稿では、実務現場でデファクトスタンダードとなっている「カスタム形式(-Fc)」の利点と、特定テーブルのピンポイント復旧をはじめとする実践的なリストア手順を整理します。
本記事の到達目標
- 論理バックアップ(
pg_dump)と物理バックアップ(pg_basebackup)の特性の違いを説明できる。 - プレーンテキスト形式(
-Fp)とカスタムアーカイブ形式(-Fc)の違いを理解し、実務でカスタム形式を選択する理由を論述できる。 pg_dumpで取得されないロールや権限などのグローバルオブジェクト(pg_dumpall -g)を漏れなく保全できる。pg_restoreを用いて、特定テーブル単体の抽出復元や、既存オブジェクトの安全なクリーン再作成(--clean --if-exists)を実行できる。- リストア時に発生しやすい代表的エラーの原因を特定し、迅速に解消できる。
検証環境情報
- ホストOS:AlmaLinux 10.2 (Lavender Lion) x86_64
- DBサーバーノード:alma-db01 (IP: 192.168.2.132 / PostgreSQL 18.6 PGDG)
- クライアントノード:alma-proxy (IP: 192.168.2.121 / psql クライアント)
目次
1. PostgreSQLバックアップの全体像:論理バックアップ vs 物理バックアップ
1.1 論理バックアップと物理バックアップの特徴比較
PostgreSQLのバックアップ手法は、大きく「論理バックアップ」と「物理バックアップ」の2つに分類されます。両者の特徴と使い分けの基準を下表に整理します。
| 比較項目 | 論理バックアップ(pg_dump / pg_dumpall) | 物理バックアップ(pg_basebackup) |
|---|---|---|
| 取得対象 | SQL文または独自バイナリ形式のデータ表現(論理構造) | データディレクトリ配下のファイル群そのもの(物理ブロック) |
| 復元粒度 | テーブル単位、スキーマ単位、単一DB単位で柔軟に抽出可能 | クラスタ全体(全データベース一括)のみ復元可能 |
| バージョン互換性 | 高い(旧バージョンから新バージョンへのメジャー移行に利用可) | なし(同一メジャーバージョン・同一アーキテクチャ限定) |
| 実行時負荷と所要時間 | データ全件をSELECTするためデータ規模に比例して増大 | ディスクI/Oに依存(大容量DBでも比較的高速にコピー可能) |
| 任意時点復旧(PITR) | 非対応(バックアップ取得時点の状態にのみ復元) | 対応(WALアーカイブと組み合わせて秒単位の復元が可能) |
1.2 pg_dump が適しているユースケース
論理バックアップ(pg_dump)は、以下のようなシーンにおいて第一の選択肢となります。
- 小〜中規模データベース(数十GB程度まで)の日次保全:ストレージ容量を抑えつつ、簡潔に世代管理を行いたい場合。
- 開発・検証環境へのデータ複製:本番環境のデータをマスク処理したり、特定のテーブル群のみを別サーバーへ移植したい場合。
- PostgreSQLのメジャーバージョンアップ:古いバージョンのクラスタから新バージョンのクラスタへデータを移行する最も確実な手法。
1.3 重要な制約:pg_dump はロールやテーブル空間を含まない
インフラエンジニアが陥りやすい最大の落とし穴の一つが、「pg_dumpで取得できるのは単一データベース内のオブジェクトに限られる」という点です。
前回の記事で作成したようなロール(ユーザー情報やパスワード)、ロール間の所属関係、およびテーブル空間(Tablespace)といった情報は、クラスタ全体で共有される「グローバルオブジェクト」として管理されています。これらはpg_dumpの出力には一切含まれません。
そのため、サーバーの全損時などに完全な復元を行うには、データベース個別のpg_dumpに加えて、グローバルオブジェクトを抽出するpg_dumpall -gコマンドを定期実行しておく必要があります。
-- クラスタ全体のロールおよびテーブル空間の定義のみをSQLとして抽出
$ sudo -u postgres /usr/pgsql-18/bin/pg_dumpall -g -f /var/lib/pgsql/globals.sql
2. pg_dump の出力フォーマット完全比較:なぜ -Fc が実務標準なのか
2.1 4つの出力形式
pg_dumpコマンドには、-F(--format)オプションで指定可能な4つの出力フォーマットが用意されています。
- p(plain / プレーンテキスト):デフォルトの形式。人間が読める平文のSQLスクリプトファイル(.sql)を出力します。
- c(custom / カスタム形式):PostgreSQL独自の圧縮バイナリ形式(.dump)。目録(TOC)情報を内包します。
- d(directory / ディレクトリ形式):テーブルやラージオブジェクトごとに個別ファイルとして分割出力する形式。並列ダンプ(
-j)に対応します。 - t(tar形式):tarアーカイブとして出力する形式(機能的な制約が多く、現在は非推奨)。

2.2 カスタム形式(-Fc)が選ばれる3大理由
本番実務において、特別な理由がない限りプレーンテキスト形式(-Fp)ではなくカスタム形式(-Fc)を採用すべき理由は以下の3点に集約されます。
- 標準でデータ圧縮が適用される:内部で自動的にgzip圧縮が行われるため、平文のSQLファイルに比べてディスク消費量を大幅に削減できます。
- 特定オブジェクトの選択的リストアが可能:ダンプファイル内に目録(TOC: Table of Contents)が保持されているため、後から「特定のテーブルだけ」「定義(スキーマ)だけ」といった部分復元を
pg_restoreで自在に指定できます。 - 依存関係の自動並べ替えと並列リストア:プレーンSQLではファイル内に記述された行順でしか実行できませんが、カスタム形式であれば
pg_restoreがテーブル・制約・インデックスの依存関係を解釈し、最適な順序で作成します。さらにマルチスレッドによる並列リストア(-j)も利用可能です。
2.3 実務で多用する pg_dump の主要オプション一覧
| オプション | ロング名 | 説明 |
|---|---|---|
-d [DB名] |
--dbname |
バックアップ対象のデータベース名。 |
-f [ファイル名] |
--file |
出力先のファイルパス。 |
-F c |
--format=custom |
カスタムアーカイブ形式で出力。 |
-v |
--verbose |
詳細な進行メッセージを出力(ログ記録に推奨)。 |
-n [スキーマ名] |
--schema |
指定したスキーマのみを対象とする。 |
-t [テーブル名] |
--table |
指定したテーブルのみを対象とする。 |
-s |
--schema-only |
テーブル定義などのDDLのみを抽出し、データは除外する。 |
-a |
--data-only |
データ行(INSERT/COPY)のみを抽出し、テーブル定義は除外する。 |
-j [並列数] |
--jobs |
複数テーブルを並列ダンプ(ディレクトリ形式 -Fd でのみ使用可能)。 |
2.3 pg_dump のロック仕様とオンライン業務への影響
データベース運用管理者が最も懸念するのは、「バックアップ取得が本番稼働中のWebアプリケーションや業務トランザクションをブロックしないか」という点です。PostgreSQLの pg_dump は、トランザクション分離レベル REPEATABLE READ(または SERIALIZABLE)を用いて、バックアップ開始時点の静止点スナップショットを論理的に切り出します。
この処理において、pg_dump はバックアップ対象となる各テーブルに対して ACCESS SHARE ロック を取得します。
ACCESS SHARE ロックの共存性と競合関係
- 業務クエリとの共存:
SELECT,INSERT,UPDATE,DELETEとは一切競合しません。つまり、バックアップ取得中であっても、ユーザーの検索や注文データの登録・更新処理は通常通り無停止で継続できます。 - DDL操作との競合・ブロッキング:
ALTER TABLE,DROP TABLE,TRUNCATE,VACUUM FULL,REINDEX TABLEなどの排他ロック(ACCESS EXCLUSIVE)を要求するコマンドとは真っ向から競合します。
仮にバックアップ取得中に夜間バッチでテーブル定義変更(ALTER TABLE)が発行されると、ALTER TABLEはpg_dumpの完了を待ってキューで待機(Lock Wait)します。さらに深刻な問題として、待機中のALTER TABLEの後ろに到着した通常のSELECT/INSERTクエリまで連鎖的にブロッキングされる「ロック待ち行列の玉突き事故」が発生します。したがって、pg_dumpを実行する時間帯にはDDLバッチやVACUUM FULLのスケジュールを絶対に重ねない運用設計が必須です。
2.4 並列ダンプ(-j)とディレクトリ形式(-Fd)の選定基準
カスタム形式(-Fc)は単一の圧縮アーカイブファイルを出力するため、ファイル管理が容易で実務の主流ですが、バックアップ取得処理そのものを並列化(マルチプロセス化)することはできません(単一ファイルへの同時書き込みが不可能なため)。
数十GB〜数百GB規模のデータベースにおいて、夜間バックアップ時間を大幅に短縮したい場合には、ディレクトリ形式(-Fd) と 並列ジョブ数(-j / --jobs) の組み合わせを採用します。
# CPUコア数に応じた並列ダンプ実行例(4ジョブ並列)
$ pg_dump -Fd -j 4 -d production_db -f /backup/prod_parallel_dir/
ディレクトリ形式では、指定したフォルダ内にテーブルごとのデータが個別の圧縮ファイルとして分割保存され、全体のメタデータが toc.dat に記録されます。各ワーカースレッドが別々のテーブルを独立してダンプするため、CPUとディスクI/Oの能力を最大限に引き出すことができます。一方、バックアップ成果物がフォルダ全体となるため、S3や外部バックアップストレージへ転送する際は tar アーカイブ化が必要になるという運用上のトレードオフがあります。
3. pg_restore によるリストア戦略と重要オプション
カスタム形式(-Fc)で採取されたバックアップファイルは、専用ツールであるpg_restoreを用いて復元します。

3.1 既存オブジェクトの上書き再作成:-c と –if-exists
すでに同名のテーブルやスキーマが存在しているデータベースに対してそのままリストアを実行すると、「テーブルが既に存在します」という重複エラーが発生して処理が中断します。全体をクリーンに上書き再構築したい場合は、-c(--clean)と--if-existsを併用します。
$ pg_restore -d production_db --clean --if-exists backup.dump
-cを指定すると、各オブジェクトを作成する直前にDROP TABLEなどの削除文が自動発行されます。さらに--if-existsを付与することで、新規データベースなど「削除対象のオブジェクトがまだ存在しない」環境でも、無用なDROPエラーを出さずに安全に処理を進めることができます。
3.2 環境移行時の権限エラー回避:-O と -x
本番環境で採取したダンプを開発環境や検証環境(ステージング)へリストアする際、本番固有のロールが存在しないことが原因でエラーが多発します。このトラブルを防止するのが以下の2つのオプションです。
- -O(–no-owner):テーブルやビューの所有者を変更する
ALTER ... OWNER TOコマンドの発行を抑制します。リストアを実行した接続ユーザーがそのまま所有者になります。 - -x(–no-privileges):個別のアクセス権限(
GRANT / REVOKE)の復元を抑制します。
$ pg_restore -d staging_db --no-owner --no-privileges production_db.dump
3.3 特定テーブル単体のピンポイント抽出復元:-t
運用中の人為的ミスで「特定のテーブル1つだけを誤って削除・更新してしまった」という緊急事態では、データベース全体を巻き戻すことなく、対象テーブルのみをピンポイントで復旧できます。
$ pg_restore -d production_db -t orders production_db.dump
ダンプファイル全体の中からordersテーブルのスキーマ定義とデータのみが検索され、投入されます。他の健全なテーブルに一切影響を与えずにトラブルを収束させることが可能です。
4. 【実機検証】バックアップ採取・データ破壊・障害復旧シミュレーション
実機環境(alma-db01)を用いて、カスタムバックアップの採取、テーブル誤削除、そしてpg_restoreによる復元プロセスを実演します。
4.1 検証用データの投入と初期状態確認
データベースproduction_db内に、顧客テーブル(customers)と注文テーブル(orders)を作成し、外部キー制約とテストデータを登録します。
$ sudo -u postgres /usr/pgsql-18/bin/psql -d production_db
production_db=# SELECT * FROM app_schema.customers;
customer_id | name | email | created_at
-------------+-----------+--------------------+-------------------------------
1 | 佐藤 太郎 | sato@example.com | 2026-09-09 20:29:56.187704+09
2 | 鈴木 次郎 | suzuki@example.com | 2026-09-09 20:29:56.187704+09
3 | 田中 花子 | tanaka@example.com | 2026-09-09 20:29:56.187704+09
(3 行)
production_db=# SELECT * FROM app_schema.orders;
order_id | customer_id | product_name | amount | order_date
----------+-------------+------------------------------+--------+-------------------------------
1 | 1 | PostgreSQL入門書 | 3200 | 2026-09-09 20:29:56.189582+09
2 | 1 | SQLハンドブック | 2800 | 2026-09-09 20:29:56.189582+09
3 | 2 | Linuxサーバー構築ガイド | 3500 | 2026-09-09 20:29:56.189582+09
4 | 3 | ネットワーク設計入門 | 4000 | 2026-09-09 20:29:56.189582+09
5 | 3 | データベースチューニング詳解 | 4800 | 2026-09-09 20:29:56.189582+09
(5 行)
4.2 pg_dump によるカスタムアーカイブの採取
カスタム形式(-Fc)と詳細表示(-v)を指定してバックアップを実行します。同時にグローバルオブジェクトも保全します。
# カスタムアーカイブの採取
$ sudo -u postgres /usr/pgsql-18/bin/pg_dump -Fc -v -d production_db -f /var/lib/pgsql/production_db_custom.dump
# グローバルオブジェクトの採取
$ sudo -u postgres /usr/pgsql-18/bin/pg_dumpall -g -f /var/lib/pgsql/globals.sql
採取されたアーカイブの内容は、pg_restore -lコマンドを実行することで、実データベースに流し込む前に目録(TOC)として閲覧できます。
$ sudo -u postgres /usr/pgsql-18/bin/pg_restore -l /var/lib/pgsql/production_db_custom.dump
; Archive created at 2026-09-09 20:30:02 JST
; dbname: production_db
; TOC Entries: 21
; Compression: gzip
; Format: CUSTOM
6; 2615 16385 SCHEMA - app_schema postgres
221; 1259 16387 TABLE app_schema customers postgres
223; 1259 16402 TABLE app_schema orders postgres
3438; 0 16387 TABLE DATA app_schema customers postgres
3440; 0 16402 TABLE DATA app_schema orders postgres
3288; 2606 16413 CONSTRAINT app_schema orders orders_pkey postgres
3289; 2606 16414 FK CONSTRAINT app_schema orders orders_customer_id_fkey postgres
4.3 障害シミュレーション:テーブルの誤削除
運用管理者が誤ってordersテーブルを破棄してしまった状況を再現します。
$ sudo -u postgres /usr/pgsql-18/bin/psql -d production_db -c "DROP TABLE app_schema.orders;"
DROP TABLE
$ sudo -u postgres /usr/pgsql-18/bin/psql -d production_db -c "\dt app_schema.*"
テーブル一覧
スキーマ | 名前 | タイプ | 所有者
------------+-----------+----------+----------
app_schema | customers | テーブル | postgres
(1 行)
テーブル一覧からordersが消失し、データが完全に失われた状態になりました。
4.4 pg_restore によるテーブル単体ピンポイント復旧の実演
採取しておいたカスタムダンプから、-t ordersオプションを指定してordersテーブルのみをリストアします。
実行手順
pg_restoreに-d production_dbと-t ordersを指定して実行します。psqlでテーブルが再作成され、5件の注文レコードが復元されたことを照合します。
$ sudo -u postgres /usr/pgsql-18/bin/pg_restore -v -d production_db -t orders /var/lib/pgsql/production_db_custom.dump
pg_restore: リストアのためデータベースに接続しています
pg_restore: TABLE "app_schema.orders"を作成しています
pg_restore: テーブル"app_schema.orders"のデータを処理しています
$ sudo -u postgres /usr/pgsql-18/bin/psql -d production_db -c "SELECT count(*) FROM app_schema.orders;"
count
-------
5
(1 行)
健全なcustomersテーブルには一切手を触れず、欠損したordersテーブルのみが迅速に復元されました。
4.5 よくあるトラブル検証:プレーン形式ファイルを pg_restore で実行した失敗例
現場でよく見られる初歩的トラブルとして、.sql(プレーンテキスト形式)として保存されたダンプファイルを誤ってpg_restoreコマンドに指定してしまうケースがあります。
$ sudo -u postgres /usr/pgsql-18/bin/pg_restore -d production_db /var/lib/pgsql/production_db_plain.sql
pg_restore: エラー: 入力ファイルがテキスト形式のようです。psqlを使用してください。
pg_restoreはカスタム形式(バイナリ)専用のツールであるため、平文SQLを読み込ませるとヘッダーチェックで弾かれます。プレーンテキストファイル(.sql)をリストアする際は、必ずpsql -f [ファイル名]を使用してください。
4.6 ディレクトリ形式(-Fd)による並列ダンプ・並列リストア実証
テーブル数が多くデータサイズが大きい環境において、処理時間を大幅に短縮する並列ダンプを実機検証します。
# 1. ディレクトリ形式かつ2並列(-j 2)でダンプ採取
$ sudo -u postgres /usr/pgsql-18/bin/pg_dump -Fd -j 2 -v -d production_db -f /var/lib/pgsql/prod_dir_dump/
pg_dump: 読み込み中 データベース "production_db"
pg_dump: スキーマ "app_schema" を読み込み中
pg_dump: テーブル "customers" を読み込み中
pg_dump: テーブル "orders" を読み込み中
pg_dump: テーブル "app_schema.customers" のデータを保存しています
pg_dump: テーブル "app_schema.orders" のデータを保存しています
# 2. 生成されたディレクトリ構造の確認
$ ls -la /var/lib/pgsql/prod_dir_dump/
合計 24
drwxr-xr-x. 2 postgres postgres 4096 9月 9 20:30 .
drwx------. 6 postgres postgres 4096 9月 9 20:30 ..
-rw-r--r--. 1 postgres postgres 320 9月 9 20:30 3280.dat.gz
-rw-r--r--. 1 postgres postgres 412 9月 9 20:30 3281.dat.gz
-rw-r--r--. 1 postgres postgres 5124 9月 9 20:30 toc.dat
各テーブルの生データが個別の圧縮ファイル(.dat.gz)として分割出力され、カタログ構造が toc.dat に保存されています。リストア時にも同様に -j 2 を指定することで、複数テーブルのインデックス作成やデータ投入をマルチコアCPUで並列実行できます。
# 並列リストアの実行例(2ジョブ並列)
$ sudo -u postgres /usr/pgsql-18/bin/pg_restore -j 2 -d production_db --clean --if-exists /var/lib/pgsql/prod_dir_dump/
5. 実務トラブルシューティング事例集(3大障害シナリオと復旧手順)
本番運用現場でバックアップおよびリストア作業を実施する際、管理者が遭遇しやすい典型的なトラブル3選と、その技術的背景・回避手順を整理します。
5.1 トラブル1:ロール不在による所有者エラー(role does not exist)
開発環境や検証環境へダンプを復元する際、最も頻発するのがロール未作成エラーです。
pg_restore: エラー: 処理中にエラーが発生しました:
ERROR: ロール "prod_db_owner" は存在しません
ALTER TABLE app_schema.customers OWNER TO prod_db_owner;
根本原因と2つの解決アプローチ
- アプローチA(所有権変更をスキップ):復元先環境でオブジェクト所有者を統一する必要がない場合は、
pg_restore -O(--no-owner)を指定してリストアを実行します。これによりALTER ... OWNER TOコマンドの発行がすべて抑止されます。 - アプローチB(事前にグローバルオブジェクトを復元):本番と同一のロール体系を再現したい場合は、テーブルリストアを実行する前に、本番で採取した
pg_dumpall -gのSQLをpsql -f globals.sqlで流し込み、ロールと表領域を先行作成します。
5.2 トラブル2:ネットワーク遮断・TCPアイドルタイムアウトによる長時間ダンプの中断
数億件の大規模テーブルをダンプする際、途中で接続が突然切断されてバックアップが異常終了するトラブルがあります。よく「statement_timeout による切断」と誤解されがちですが、pg_dump は接続確立時に内部で SET statement_timeout = 0、SET idle_in_transaction_session_timeout = 0、SET lock_timeout = 0 を自動発行して自身のセッションタイムアウトを無効化する仕様になっています。
したがって、サーバー設定のタイムアウト値によってダンプが強制終了することはありません。長時間ダンプが切断される真因の9割以上は、クライアントとDBサーバー間に介在するファイアウォール、クラウドNATゲートウェイ、ロードバランサーによる「TCPアイドルタイムアウト(無通信切断)」、またはバックアップ先ストレージのディスク容量枯渇です。
長時間ダンプの中断を防ぐ実践対策
- TCPキープアライブの明示設定:中継機器が「無通信セッション」と誤認してTCPコネクションを破棄するのを防ぐため、接続パラメータでTCPキープアライブを短間隔で送信するよう指定します。
# TCPキープアライブ間隔を短縮して切断を抑止 $ pg_dump "host=192.168.2.132 dbname=production_db user=postgres keepalives=1 keepalives_idle=60 keepalives_interval=10 keepalives_count=5" -Fc -f /backup/prod_full.dump - 出力先ディスク容量の事前監視:ダンプ途中でディスク使用率が100%に達すると、不完全な破損ダンプが生成されて終了します。
df -h /backupで十分な空き領域を事前に確認します。
5.3 トラブル3:外部キー制約の循環参照によるリストアエラー
親テーブルと子テーブルが相互に外部キー(FOREIGN KEY)で参照し合っている場合や、リストアの並列実行順序によって「参照先レコードがまだ投入されていない」タイミングで子テーブルへのデータ投入が発生すると、外部キー違反エラー(FK Constraint Violation)が発生します。
この事態を防ぐため、pg_restore には --disable-triggers オプションが用意されています。このオプションを指定すると、データ投入の直前に一時的に外部キートリガーを無効化し、全データの投入が完了した後にトリガーを再有効化します(※実行にはスーパーユーザー権限が必要です)。
【重要】–disable-triggers の動作要件
--disable-triggers オプションは、データのみをリストアする --data-only(-a)指定時のみ有効に機能します。テーブル定義(スキーマ)を含む完全リストア時に指定してもPostgreSQLによって無視されます。外部キーの依存循環があるテーブル群のデータのみを入れ替えるリストア作業において、-a と組み合わせて使用します。
# データのみリストア時に外部キートリガーを一時無効化して投入
$ sudo -u postgres /usr/pgsql-18/bin/pg_restore -d production_db -a --disable-triggers /backup/prod_custom.dump
6. 実務チートシート:日次自動バックアップスクリプトの実装例
本番サーバーでcron等に登録して運用できる、世代管理・容量監視・整合性検査・エラーハンドリングを備えた日次バックアップシェルスクリプトの標準実装例を提供します。
実務スクリプトに組み込むべき4大必須要件
- 事前容量チェック:バックアップ先パーティションの空き容量が不足している状態でダンプを開始すると、ディスク枯渇を引き起こして本番DB本体を巻き添え停止させる危険があります。事前に最低必要容量(例:10GB以上)を検査します。
- ダンプ整合性検査(ベリファイ):出力されたダンプファイルが破損していないかを
pg_restore -lで即座に構文チェックし、ヘッダー破損や途中で切断された異常ファイルを検知します。 - グローバルオブジェクトの完全保全:単一データベースのダンプだけでなく、クラスタ共通のログインロールやテーブル空間情報を
pg_dumpall -gで同時保全します。 - 世代パージの安全性:バックアップが正常完了したことを確認した後にのみ、保存期間(例:7日間)を超過した過去世代ファイルを削除します。
#!/bin/bash
set -euo pipefail
# ==============================================================================
# PostgreSQL 日次自動論理バックアップスクリプト
# ==============================================================================
PG_BIN_DIR="/usr/pgsql-18/bin"
BACKUP_DIR="/var/backups/postgresql"
LOG_FILE="/var/log/postgresql_backup.log"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=7
DB_NAME="production_db"
MIN_FREE_KB=$((10 * 1024 * 1024)) # 10GB 以上の空きが必要
mkdir -p "${BACKUP_DIR}"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "${LOG_FILE}"
}
log "INFO: バックアップ処理を開始します (対象DB: ${DB_NAME})"
# 1. バックアップ先ディスクの空き容量チェック
FREE_KB=$(df -P "${BACKUP_DIR}" | awk 'NR==2 {print $4}')
if [ "${FREE_KB}" -lt "${MIN_FREE_KB}" ]; then
log "ERROR: バックアップ領域の空き容量が不足しています (空き: ${FREE_KB} KB, 必要: ${MIN_FREE_KB} KB)"
exit 1
fi
# 2. データベース個別ダンプ(カスタム形式・圧縮)
DUMP_FILE="${BACKUP_DIR}/${DB_NAME}_${DATE}.dump"
log "INFO: pg_dump を実行中 -> ${DUMP_FILE}"
"${PG_BIN_DIR}/pg_dump" -Fc -v -d "${DB_NAME}" -f "${DUMP_FILE}" >> "${LOG_FILE}" 2>&1
# 3. ダンプファイルの整合性検証(目次抽出テスト)
log "INFO: ダンプファイルの整合性を検証中..."
if ! "${PG_BIN_DIR}/pg_restore" -l "${DUMP_FILE}" > /dev/null 2>&1; then
log "FATAL: 生成されたダンプファイルが破損しています。"
rm -f "${DUMP_FILE}"
exit 2
fi
log "INFO: ダンプファイルの整合性検証に合格しました。"
# 4. グローバルオブジェクト保全(ロール・テーブル空間)
GLOBALS_FILE="${BACKUP_DIR}/globals_${DATE}.sql"
log "INFO: pg_dumpall -g を実行中 -> ${GLOBALS_FILE}"
"${PG_BIN_DIR}/pg_dumpall" -g -f "${GLOBALS_FILE}" >> "${LOG_FILE}" 2>&1
# 5. 保存期間を超過した古い世代の安全な削除
log "INFO: ${RETENTION_DAYS} 日以前の古いバックアップ世代を整理中..."
find "${BACKUP_DIR}" -name "${DB_NAME}_*.dump" -mtime +${RETENTION_DAYS} -delete
find "${BACKUP_DIR}" -name "globals_*.sql" -mtime +${RETENTION_DAYS} -delete
log "SUCCESS: バックアップ処理が正常に完了しました。"
6.1 cron登録時の環境変数とパスの留意点
上記スクリプトを crontab に登録して夜間自動実行する際、最も多いトラブルが「手動実行では動くが、cron経由だとコマンドが見つからない(command not found)」というエラーです。
cron実行環境では、対話型シェルと異なり極めて限定的な PATH 環境変数しかロードされません。スクリプト内で /usr/pgsql-18/bin/ のようにフルパスでバイナリを指定するか、スクリプトの冒頭で明示的に export PATH="/usr/pgsql-18/bin:$PATH" を宣言することが必須です。
# /etc/cron.d/postgres_backup の設定例(毎日深夜2時00分に実行)
0 2 * * * postgres /usr/local/bin/postgres_backup.sh >> /var/log/cron_postgres.log 2>&1
7. 理解度確認演習問題(全3問・解答解説付き)
第1問:pg_dump の出力形式とリストアツールの関係
以下の組み合わせのうち、正しいものを1つ選択してください。
pg_dump -Fpで出力したファイルをpg_restore -d dbname backup.sqlで復元した。pg_dump -Fcで出力したファイルをpg_restore -d dbname backup.dumpで復元した。pg_dumpallで出力したファイルをpg_restore -d dbname all.sqlで復元した。pg_dump -Fcで出力したファイルをpsql -d dbname -f backup.dumpで復元した。
解答と解説を見る
正解:2
解説:カスタム形式(-Fc)で出力されたアーカイブは、専用コマンドであるpg_restoreを用いてリストアします。プレーンテキスト形式(-Fpやpg_dumpall)は平文SQLであるため、psql -fで実行する必要があります。選択肢1や3のように平文SQLをpg_restoreに渡すとヘッダーエラーが発生します。
第2問:リストアオプションの役割
本番環境で採取したカスタムダンプファイルを、別サーバー上のステージング環境(staging_db)へリストアしようとしています。本番環境に存在するロールprod_adminがステージング環境には存在しないため、所有者変更エラーを抑制して安全に復元したいと考えています。このときに指定すべき最適なオプションを選択してください。
--clean--if-exists--no-owner(-O)--data-only(-a)
解答と解説を見る
正解:3
解説:--no-owner(短縮形: -O)は、オブジェクトの所有者を元々のロールに変更するSQL出力をスキップします。これにより、移行先環境に対象ロールが存在しなくてもエラーにならず、リストアを実行したユーザーの所有としてオブジェクトが復元されます。
第3問:pg_dump の保全範囲に関する記述
pg_dumpコマンドの仕様に関する記述として、最も適切なものを選択してください。
pg_dumpを実行すると、指定したデータベース内のオブジェクトだけでなく、ログインユーザーやパスワード情報もすべて保全される。pg_dumpではロール情報やテーブル空間情報は保全されないため、クラスタ全体の再構築に備えるにはpg_dumpall -gを併用する必要がある。pg_dumpはデータベースサーバーを完全に停止させた状態(コールド状態)でなければ実行できない。pg_dumpで出力したファイルから単一のテーブルのみを復元することは、いかなる出力形式であっても不可能である。
解答と解説を見る
正解:2
解説:pg_dumpは単一データベースの論理構造をバックアップするツールであり、クラスタ全体で共有されるロールやテーブル空間などのグローバルオブジェクトは含まれません。したがって、完全な復旧要件を満たすにはpg_dumpall -gを組み合わせて保全するのが必須の運用設計です。
8. まとめと次回予告
- 論理バックアップでは、データ圧縮・目録抽出・並列リストアが可能な「カスタム形式(
-Fc)」を採用するのが実務標準である。 pg_dumpは単一データベースのみが対象であるため、クラスタ共通のロール保全にはpg_dumpall -gの併用が不可欠である。- 既存環境への上書きには
--clean --if-exists、別環境への移行には--no-owner --no-privilegesを活用する。 - 人為的なデータ消失障害に対しては、
pg_restore -tによるピンポイント復旧が最も迅速な復旧手段となる。 pg_dumpはタイムアウトパラメータ群を自動で無効化するため、長時間ダンプの切断防止にはTCPキープアライブやディスク空き容量管理を適用する。
次回は第6回「【MVCC内部構造と保守】VACUUMとANALYZEによる肥大化防止と性能維持」を解説します。更新・削除で発生する不要タプルのメカニズム、autovacuumの自動起動閾値の計算式、本番環境でVACUUM FULLが原則厳禁とされる理由、そして緊急Freeze対策を実機検証で整理します。
PostgreSQL 18 構築・運用ガイド
- 前の記事: 第4回「ロール・権限管理とデータベース作成のベストプラクティス」
- 現在の記事: 第5回「【論理バックアップ実務】pg_dumpとpg_restoreによるバックアップ・リストア完全手順」
- 次の記事: 第6回「【MVCC内部構造と保守】VACUUMとANALYZEによる肥大化防止と性能維持」
- シリーズ企画・目次: シリーズ総合ロードマップとカリキュラム一覧
Kindle書籍版(第8章)のご案内:テラバイト級DBの物理バックアップとPITR
論理バックアップ(pg_dump)は非常に柔軟ですが、データベースの規模が数百GB〜数TBに達すると、バックアップやリストアに長時間を要し、運用が破綻します。商業出版予定のKindle書籍版『現場で困らない PostgreSQL 本番運用大全 —— 4ノード実機で作る高可用性・チューニング・障害復旧』第8章では、エンタープライズ規模で必須となる以下の本格運用を網羅します。
- pg_basebackup による無停止物理バックアップ:テラバイト規模の大規模クラスタを無停止で瞬時に保全するベースバックアップの設計と自動化。
- WALアーカイブログの多重化保全:トランザクションログ(WAL)をS3やNFSストレージへ安全に連続転送するアーカイビング設計。
- ポイントインタイムリカバリ(PITR)の実践:「午前10時32分15秒に実行された誤操作」の直前(10時32分14秒)のトランザクション時点へ、1秒の狂いもなくデータベース全体を巻き戻す厳密なデータ復旧手順書。
