PostgreSQL: PostgreSQLバックアップ、復元、高可用性
最終更新:2026-08-26
1. 学習内容
- pg_dump / pg_dumpall論理バックアップ
- pg_restore復元
- COPYコマンドによるインポート/エクスポート(CSV / バイナリ)
- WAL(先行書き込みログ)の原理
- PITRポイントインタイムリカバリ(PGの特長)
- ストリーミングレプリケーション
- 論理レプリケーション(PGの特長:テーブル単位でレプリケート)
- pg_basebackup物理バックアップ
- バックアップ��略:フル+増分
2. ストーリー
BobはEコマースプラットフォームのDBAです。午前3時、開発者が誤って DELETE FROM products WHERE category = 'Electronics' を実行し、200万ドル相当の商品データが削除されました。
Bobは誤削除の1分前である午前2時59分の状態に、データ損失ゼロで復旧する必要があります。彼はPITR(ポイントインタイムリカバリ)ソリューションを選択します。
3. 概念:論理バックアップ
(1) pg_dumpのオプション
| オプション | 意味 | 例 |
|---|---|---|
-Fc |
カスタム形式(圧縮、推奨) | pg_dump -Fc db > db.dump |
-Fd |
ディレクトリ形式(並列バックアップ) | pg_dump -Fd db -f dir/ |
-Fp |
プレーンSQLテキスト | pg_dump -Fp db > db.sql |
-j N |
並列ジョブ数(-Fdが必要) | pg_dump -Fd db -j 4 -f dir/ |
-t table |
指定テーブルのみバックアップ | pg_dump -t products db > p.dump |
-n schema |
指定スキーマのみバックアップ | pg_dump -n public db > s.dump |
--exclude-table |
テーブルを除外 | pg_dump --exclude-table=logs db > d.dump |
-Z 0-9 |
圧縮レベル | pg_dump -Fc -Z6 db > db.dump |
▶ サンプル:pg_dumpで単一データベースをバックアップ
# カスタム形式(推奨、圧縮、並列復元可能)
pg_dump -h localhost -U postgres -Fc shop_db > /backup/shop_db_$(date +%Y%m%d).dump
# 4並列ワーカーでのディレクトリ形式
pg_dump -h localhost -U postgres -Fd shop_db -j 4 -f /backup/shop_db_dir/
# プレーンSQL形式(人間が読める、復元前に編集可能)
pg_dump -h localhost -U postgres -Fp shop_db > /backup/shop_db.sql
Output:
# コマンドが正常に実行されました
▶ サンプル:pg_dumpで特定のテーブルをバックアップ
# ordersテーブルとorder_itemsテーブルのみバックアップ
pg_dump -h localhost -U postgres -Fc \
-t orders -t order_items \
shop_db > /backup/order_tables.dump
# パターンに一致するすべてのテーブルをバックアップ
pg_dump -h localhost -U postgres -Fc \
-t 'order_*' \
shop_db > /backup/order_prefix_tables.dump
# 大規模なログテーブルを除外
pg_dump -h localhost -U postgres -Fc \
--exclude-table='access_log_*' \
shop_db > /backup/shop_no_logs.dump
Output:
# コマンドが正常に実行されました
(2) pg_dumpall vs pg_dump
| 観点 | pg_dump | pg_dumpall |
|---|---|---|
| バックアップ範囲 | 単一データベース | クラスタ全体(全データベース) |
| ロール情報 | 含まれない | ロール/テーブルスペース定義を含む |
| 出力形式 | オプションで-Fc/-Fd/-Fp | プレーンSQLのみ(-Fp) |
| 並列処理 | サポート(-j) | 非サポート |
| 推奨シナリオ | 日次の単一DBバックアップ | ロールバックアップ+クラスタ全体移行 |
▶ サンプル:pg_dumpallでクラスタ全体をバックアップ
# 全データベースとロールをバックアップ(プレーンSQLのみ)
pg_dumpall -h localhost -U postgres > /backup/cluster_full_$(date +%Y%m%d).sql
# ロールのみバックアップ(移行に便利)
pg_dumpall -h localhost -U postgres --roles-only > /backup/roles_only.sql
# テーブルスペース定義のみバックアップ
pg_dumpall -h localhost -U postgres --tablespaces-only > /backup/tablespaces.sql
Output:
# コマンドが正常に実行されました
4. 概念:論理復元
(1) pg_restoreのオプション
| オプション | 意味 | 適用形式 |
|---|---|---|
-d db |
指定データベースに復元 | -Fc / -Fd |
-j N |
並列復元 | -Fc / -Fd |
--clean |
最初にDROPしてからCREATE | -Fc / -Fd |
--if-exists |
DROP IF EXISTS(--cleanと併用) | -Fc / -Fd |
-t table |
指定テーブルのみ復元 | -Fc / -Fd |
--list |
アーカイブ内容の一覧表示 | -Fc / -Fd |
--section=pre-data |
プリデータ(スキーマ)のみ復元 | -Fc / -Fd |
▶ サンプル:カスタム形式からpg_restoreで復元
# 新しいデータベースに復元
createdb -h localhost -U postgres shop_db_restore
pg_restore -h localhost -U postgres -d shop_db_restore /backup/shop_db.dump
# 並列復元(4ワーカー、ディレクトリ形式)
pg_restore -h localhost -U postgres -d shop_db -j 4 /backup/shop_db_dir/
# クリーン復元(既存オブジェクトを先に削除)
pg_restore -h localhost -U postgres -d shop_db \
--clean --if-exists /backup/shop_db.dump
Output:
# コマンドが正常に実行されました
▶ サンプル:選択的復元
# アーカイブ内容を一覧表示してテーブルエントリ番号を確認
pg_restore --list /backup/shop_db.dump
# 名前で特定のテーブルのみ復元
pg_restore -h localhost -U postgres -d shop_db \
-t products -t categories \
/backup/shop_db.dump
# スキーマのみ復元(データなし)
pg_restore -h localhost -U postgres -d shop_db \
--section=pre-data /backup/shop_db.dump
Output:
# コマンドが正常に実行されました
(2) pg_restore vs psql < file.sql
| 観点 | pg_restore | psql < file.sql |
|---|---|---|
| 入力形式 | -Fc / -Fd | プレーンSQLテキスト |
| 並列処理 | サポート(-j) | 非サポート |
| 選択的復元 | サポート(-t / --list) | 非サポート |
| 古いデータのクリーンアップ | --clean | 手動でDROPを記述必要 |
| 推奨シナリオ | カスタム / ディレクトリ形式 | pg_dumpall出力 |
▶ サンプル:プレーンSQLからの復元
# pg_dumpall出力からの復元
psql -h localhost -U postgres -f /backup/cluster_full_20250601.sql
# pg_dumpプレーン形式からの復元
createdb -h localhost -U postgres shop_db_new
psql -h localhost -U postgres -d shop_db_new -f /backup/shop_db.sql
Output:
# psqlコマンドが正常に実行されました
5. 概念:COPYインポート/エクスポート
(1) COPY vs \copy
| コマンド | 実行場所 | ファイルアクセス | 必要な権限 |
|---|---|---|---|
COPY(SQL) |
サーバー側 | サーバーファイルシステムを読み取り | スーパーユーザー |
\copy(psql) |
クライアント側 | クライアントファイルを読み取り | 一般ユーザー |
▶ サンプル:COPYでCSVにエクスポート
-- ヘッダー付きCSVにエクスポート
COPY (SELECT order_id, customer_id, total_amount, order_date
FROM orders
WHERE order_status = 'completed'
ORDER BY order_date DESC)
TO '/tmp/completed_orders.csv'
WITH (FORMAT csv, HEADER true, DELIMITER ',');
-- テーブル全体をエクスポート
COPY products TO '/tmp/products.csv' WITH (FORMAT csv, HEADER true);
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
▶ サンプル:COPYでCSVをインポート
-- CSVからインポート
COPY products(product_name, unit_price, category, stock_qty)
FROM '/data/new_products.csv'
WITH (FORMAT csv, HEADER true, DELIMITER ',');
-- エラー処理付きインポート(PG 17+)
COPY products(product_name, unit_price, category, stock_qty)
FROM '/data/new_products.csv'
WITH (FORMAT csv, HEADER true, ON_ERROR ignore);
Output:
-- SQL文が正常に実行されました
(2) COPY形式オプション
| オプション | 値 | 説明 |
|---|---|---|
| FORMAT | csv / text / binary | 出力形式 |
| HEADER | true / false | 1行目が列名 |
| DELIMITER | ',' / '\t' | 区切り文字(csvのデフォルトはカンマ) |
| QUOTE | '"' | 引用符文字 |
| NULL | '' | NULLを表す文字列 |
| ENCODING | 'UTF8' | ファイルエンコーディング |
▶ サンプル:バイナリ形式のエクスポート/インポート
-- バイナリエクスポート(高速、小サイズ)
COPY orders TO '/tmp/orders.bin' WITH (FORMAT binary);
-- バイナリインポート
COPY orders FROM '/tmp/orders.bin' WITH (FORMAT binary);
Output:
-- SQL文が正常に実行されました
6. 概念:WALとPITR
(1) WAL(先行書き込みログ)の原理
WALはPGがデータ整合性を保証しリカバリをサポートする中核メカニズムです。
flowchart LR
A[クライアント<br/>WRITE] --> B[WALバッファ<br/>最初にログを書き込み]
B --> C[WALファイル<br/>ディスクに永続化]
B --> D[共有バッファ<br/>後でデータを書き込み]
D --> E[データファイル<br/>チェックポイントでディスクにフラッシュ]
style B fill:#ffcdd2
style C fill:#ff8a80
style D fill:#c8e6c9
style E fill:#a5d6a7
| 概念 | 説明 |
|---|---|
| WALセグメント | WALファイル、デフォルトで各16MB |
| LSN | ログシーケンス番号、ログ位置を識別 |
| チェックポイント | メモリ上のダーティページをディスクにフラッシュ、WALをリサイクル |
| wal_level | WALの詳細レベル:replica / logical |
| archive_mode | WALファイルをアーカイブするかどうか |
| archive_command | 指定パスにアーカイブ |
(2) PITR設定手順
| ステップ | 操作 | 説明 |
|---|---|---|
| 1 | WALアーカイブを有効化 | archive_mode = on |
| 2 | アーカイブコマンドを設定 | archive_command = 'cp %p /archive/%f' |
| 3 | ベースバックアップ | pg_basebackup |
| 4 | 継続的アーカイブ | WALファイルが自動的にアーカイブされる |
| 5 | ポイントインタイムに復旧 | recovery_target_time |
▶ サンプル:WALアーカイブの設定
# postgresql.confの設定
cat >> /etc/postgresql/16/main/postgresql.conf << 'EOF'
wal_level = replica
archive_mode = on
archive_command = 'cp %p /var/lib/postgresql/archive/%f'
archive_timeout = 300
max_wal_senders = 3
EOF
# PostgreSQLを再起動
pg_ctlcluster 16 main restart
Output:
# コマンドが正常に実行されました
▶ サンプル:pg_basebackup物理ベースバックアップ
# 完全物理バックアップ(PITRのベース)
pg_basebackup -h localhost -U replicator \
-D /backup/base_$(date +%Y%m%d) \
-Ft -z -P \
--checkpoint=fast
# -Ft: tar形式
# -z: gzip圧縮
# -P: 進捗表示
# --checkpoint=fast: バックアップ前にチェックポイントを強制
Output:
# コマンドが正常に実行されました
▶ サンプル:PITRポイントインタイムリカバリ
Bobは誤削除の1分前(午前2時59分)に復旧します:
# ステップ1: PostgreSQLを停止
pg_ctlcluster 16 main stop
# ステップ2: 既存のデータディレクトリをクリア
rm -rf /var/lib/postgresql/16/main/*
# ステップ3: ベースバックアップを復元
tar -xzf /backup/base_20250601/base.tar.gz \
-C /var/lib/postgresql/16/main/
# ステップ4: リカバリ設定を作成
cat >> /var/lib/postgresql/16/main/postgresql.auto.conf << 'EOF'
restore_command = 'cp /var/lib/postgresql/archive/%f %p'
recovery_target_time = '2025-06-01 02:59:00'
recovery_target_action = 'promote'
EOF
# ステップ5: リカバリシグナルファイルを作成
touch /var/lib/postgresql/16/main/recovery.signal
# ステップ6: PostgreSQLを起動(ターゲット時刻まで復旧)
pg_ctlcluster 16 main start
# PostgreSQLは02:59:00までWALを再生し、その後プロモートします
Output:
# コマンドが正常に実行されました
(3) 論理バックアップ vs 物理バックアップ vs PITR
| 観点 | 論理バックアップ(pg_dump) | 物理バックアップ(pg_basebackup) | PITR |
|---|---|---|---|
| 粒度 | テーブル / DB | クラスタ全体 | クラスタ全体 |
| 復旧精度 | バックアップ時点 | バックアップ時点 | 任意の時点 |
| 復旧速度 | 遅い(SQL行単位) | 高速(ファイルコピー) | 中程度(WAL再生) |
| ストレージ容量 | 小さい(圧縮) | 大きい(完全コピー) | 中程度(ベース+アーカイブ) |
| ゼロ損失 | 不可 | 不可 | 可 |
| オンラインバックアップ | 可 | 可 | 可 |
7. 概念:レプリケーションと高可用性
(1) ストリーミングレプリケーション vs 論理レプリケーション
| 観点 | ストリーミングレプリケーション | 論理レプリケーション |
|---|---|---|
| レプリケーションレベル | 物理WALブロック | 論理変更(INSERT/UPDATE/DELETE) |
| 粒度 | クラスタ全体 | 指定テーブル / パブリケーション |
| バージョン要件 | プライマリとスタンバイのバージョンが一致必須 | メジャーバージョンを跨げる |
| DDLレプリケーション | 自動 | DDLをレプリケートしない |
| ターゲットの書き込み可否 | 不可(読み取り専用スタンバイ) | 可 |
| PGの特長 | — | PGネイティブ論理レプリケーション(10+) |
▶ サンプル:ストリーミングレプリケーションの設定
# プライマリ側:postgresql.conf
wal_level = replica
max_wal_senders = 5
wal_keep_size = '1GB'
# プライマリ側:pg_hba.conf
echo 'host replication replicator 192.168.1.0/24 md5' >> pg_hba.conf
# プライマリ側:レプリケーションユーザーを作成
psql -c "CREATE USER replicator WITH REPLICATION ENCRYPTED PASSWORD 'RepP@ss';"
# スタンバイ側:ベース��ックアップを取得
pg_basebackup -h primary_host -U replicator \
-D /var/lib/postgresql/16/main -Fp -Xs -P -R
# -R: standby.signalを作成し自動設定
# スタンバイは起動時にプライマリに自動接続します
Output:
# psqlコマンドが正常に実行されました
▶ サンプル:論理レプリケーションの設定
-- パブリッシャー側(ソースデータベース)
CREATE PUBLICATION pub_orders FOR TABLE orders, order_items;
-- またはスキーマ内の全テーブルをパブリッシュ
CREATE PUBLICATION pub_all FOR ALL TABLES;
-- サブスクライバー側(ターゲットデータベース)
CREATE SUBSCRIPTION sub_orders
CONNECTION 'host=primary_host dbname=shop_db user=replicator password=RepP@ss'
PUBLICATION pub_orders;
-- レプリケーション状態を確認
SELECT * FROM pg_stat_replication; -- パブリッシャー側
SELECT * FROM pg_stat_subscription; -- サブスクライバー側
Output:
CREATE TABLE
(2) レプリケーション監視ビュー
| ビュー | 場所 | 内容 |
|---|---|---|
pg_stat_replication |
プライマリ | 接続中の全スタンバイの状態 |
pg_stat_wal_receiver |
スタンバイ | WAL受信状態 |
pg_stat_subscription |
サブスクライバー | 論理レプリケーションサブスクリプション状態 |
pg_replication_slots |
プライマリ | レプリケーションスロット情報 |
▶ サンプル:ストリーミングレプリケーション遅延の監視
-- プライマリでレプリケーション遅延を確認
SELECT
client_addr,
state,
sent_lsn,
write_lsn,
flush_lsn,
replay_lsn,
(sent_lsn - replay_lsn) AS replication_lag
FROM pg_stat_replication;
-- スタンバイがリカバリモードかを確認
SELECT pg_is_in_recovery();
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
8. 概念:バックアップ戦略
(1) フル+増分の比較
| 戦略 | 方法 | 復旧時間 | ストレージコスト | ゼロ損失 |
|---|---|---|---|---|
| 論理フル | スケジュールされたpg_dump | 遅い | 低い | 不可 |
| 物理フル | スケジュールされたpg_basebackup | 高速 | 高い | 不可 |
| PITR | ベースバックアップ+WALアーカイブ | 中程度 | 中程度 | 可 |
| ストリーミングレプリケーション | スタンバイリアルタイム同期 | 最速 | 高い | ほぼゼロ損失 |
▶ サンプル:自動バックアップスクリプト
#!/bin/bash
# shop_dbの日次バックアップスクリプト
BACKUP_DIR="/backup/daily"
DATE=$(date +%Y%m%d_%H%M%S)
RETAIN_DAYS=7
# 完全論理バックアップ(pg_dumpカスタム形式)
pg_dump -h localhost -U postgres -Fc -Z6 \
shop_db > ${BACKUP_DIR}/shop_db_${DATE}.dump
# ロールを個別にバックアップ
pg_dumpall -h localhost -U postgres --roles-only \
> ${BACKUP_DIR}/roles_${DATE}.sql
# 古いバックアップをクリーンアップ(7日間保持)
find ${BACKUP_DIR} -name "*.dump" -mtime +${RETAIN_DAYS} -delete
find ${BACKUP_DIR} -name "*.sql" -mtime +${RETAIN_DAYS} -delete
echo "バックアップ完了: shop_db_${DATE}.dump"
Output:
# コマンドが正常に実行されました
(2) 推奨される本番戦略
| 環境 | 推奨ソリューション | RPO | RTO |
|---|---|---|---|
| 開発 | pg_dump日次 | 24時間 | 数時間 |
| テスト | pg_dump+ストリーミングレプリケーション | 数分 | 数分 |
| 本番 | PITR+ストリーミングレプリケーション | ほぼゼロ | 数分 |
| 重要本番 | PITR+ストリーミングレプリケーション+論理レプリケーション | ゼロ | 数秒 |
▶ サンプル:バックアップ整合性の検証
# pg_dumpバックアップが読み取り可能か確認
pg_restore --list /backup/shop_db.dump > /dev/null
if [ $? -eq 0 ]; then
echo "バックアップ検証OK"
else
echo "バックアップ破損 - 運用チームに警告!"
fi
# WALアーカイブが遅延していないか確認
psql -c "SELECT pg_current_wal_lsn(), pg_last_archive_lsn();"
Output:
# psqlコマンドが正常に実行されました
9. フローチャート:バックアップ/復元の決定
flowchart TD
A[バックアップ/復元が必要?] --> B{復元シナリオ?}
B -->|テーブル/データ削除| C{論理バックアップあり?}
B -->|DB全体クラッシュ| D{PITRあり?}
B -->|計画移行| E{データ量?}
C -->|はい| F[pg_restore -t table]
C -->|いいえ| G{ストリーミングスタンバイあり?}
G -->|はい| H[スタンバイからエクスポート]
G -->|いいえ| I[データ復旧不可]
D -->|はい| J[ターゲット時刻にPITR]
D -->|いいえ| K[pg_basebackupのバックアップ時刻に復元]
E -->|小規模| L[pg_dump/pg_restore]
E -->|大規模| M[pg_basebackup+ストリーミング]
J --> N{クロスバージョン?}
N -->|はい| O[論理レプリケーション移行]
N -->|いいえ| P[物理リカバリ]
style I fill:#ffcdd2
style J fill:#c8e6c9
style O fill:#bbdefb
10. 総合的な例
Bobの完全なPITRリカバリフロー — 午前3時の誤ったproducts削除後、午前2時59分に復旧:
-- ステップ1: 事故時刻とデータ損失を確認
SELECT pg_current_wal_lsn(); -- 現在のLSNを記録
SELECT COUNT(*) FROM products WHERE category = 'Electronics'; -- 損失を確認
-- ステップ2: WALアーカイブに必要なセグメントがあるか確認
SELECT pg_last_archive_lsn();
-- 02:59 AM時点のLSN以上であるべき
# ステップ3: 状態を保持するため即座にPostgreSQLを停止
pg_ctlcluster 16 main stop
# ステップ4: 現在のWALファイルを保存(削除しないでください!)
cp -r /var/lib/postgresql/16/main/pg_wal /tmp/pg_wal_backup/
# ステップ5: ベースバックアップを復元
rm -rf /var/lib/postgresql/16/main/*
tar -xzf /backup/base_20250531/base.tar.gz \
-C /var/lib/postgresql/16/main/
# ステップ6: 完全なリカバリのため保存したWALを戻す
cp /tmp/pg_wal_backup/* /var/lib/postgresql/16/main/pg_wal/
# ステップ7: PITRターゲットを設定
cat >> /var/lib/postgresql/16/main/postgresql.auto.conf << 'EOF'
restore_command = 'cp /var/lib/postgresql/archive/%f %p'
recovery_target_time = '2025-06-01 02:59:00'
recovery_target_action = 'promote'
EOF
touch /var/lib/postgresql/16/main/recovery.signal
# ステップ8: 起動して検証
pg_ctlcluster 16 main start
# ステップ9: リカバリを検証
psql -c "SELECT COUNT(*) FROM products WHERE category = 'Electronics';"
-- ステップ10: リカバリ成功後、新しいバックアップを取得
-- データ整合性確認後にpsqlで実行
SELECT pg_switch_wal(); -- クリーンなアーカイブポイントのためにWAL切り替えを強制
# ステップ11: 将来のPITR用に新しいベースバックアップを取得
pg_basebackup -h localhost -U postgres \
-D /backup/base_$(date +%Y%m%d) -Ft -z -P
❓ よくある質問
recovery_target_xidで特定のトランザクションを指定できます。ただし、一部のトランザクションをスキップして特定の操作だけをロールバックすることはできません。hot_standby = onを設定すると、スタンバイは読み取り専用クエリ(SELECT)を受け付け、「読み取りレプリカ」と呼ばれ、分析クエリの負荷分散に適しています。📖 まとめ
- pg_dump論理バックアップはカスタム/ディレクトリ/プレーンSQL形式をサポートし、-Fc圧縮形式が最も一般的です
- pg_restoreはカスタム/ディレクトリ形式から復元し、並列、選択的復元、--cleanクリーンアップをサポートします
- COPY / \copyはCSVを高速にインポート/エクスポートし、COPYはサーバー上で、\copyはクライアント上で実行されます
- WALはPGリカバリの基盤であり、データより先にログを書き込み、クラッシュリカバリを保証します
- PITR(PGの特長)はベースバックアップ+WALアーカイブにより任意の時点に復旧し、データ損失ゼロを実現します
- ストリーミングレプリケーションは物理WALをリアルタイム同期し、スタンバイは読み取りレプリカとして機能します
- 論理レプリケーション(PGの特長)はテーブルレベルでレプリケートし、クロスバージョンと書き込み可能ターゲットをサポートします
- pg_basebackup物理バックアップはPITRとストリーミングレプリケーションの基盤です
- 本番環境ではPITR+ストリーミングレプリケーションの組み合わせを推奨し、定期的なバックアップ整合性チェックを行います
📝 練習問題
-
⭐
shop_dbデータベースをpg_dump -Fcでバックアップし、pg_restore --listでバックアップ整合性を検証し、shop_db_testデータベースに復元してください。 -
⭐⭐ 自動バックアップスクリプトを作成してください:日次のpg_dumpフルバックアップ(カスタム形式、圧縮)、7日間保持、バックアップ後に
pg_restore --listで検証。また、ordersテーブルをCOPYでCSVにエクスポート(ヘッダー付き)し、日付で名前を付けてください。 -
⭐⭐⭐ 完全なPITRソリューションを設定してください:WALアーカイブを有効化(
archive_commandで指定ディレクトリに設定)、pg_basebackupベースバックアップを実行、誤ってデータを削除するシミュレーション、指定時点に復旧。LSNとリカバリ時刻を記録し、データ整合性を検証してください。さらにストリーミングレプリケーションスタンバイを設定し��読み取りクエリが動作することを確認してください。