PostgreSQL: PostgreSQLバックアップ、復元、高可用性

最終更新:2026-08-26

1. 学習内容


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で単一データベースをバックアップ

BASH
# カスタム形式(推奨、圧縮、並列復元可能)
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:

TEXT 📖 参照専用
# コマンドが正常に実行されました

▶ サンプル:pg_dumpで特定のテーブルをバックアップ

BASH
# 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:

TEXT 📖 参照専用
# コマンドが正常に実行されました

(2) pg_dumpall vs pg_dump

観点 pg_dump pg_dumpall
バックアップ範囲 単一データベース クラスタ全体(全データベース)
ロール情報 含まれない ロール/テーブルスペース定義を含む
出力形式 オプションで-Fc/-Fd/-Fp プレーンSQLのみ(-Fp)
並列処理 サポート(-j) 非サポート
推奨シナリオ 日次の単一DBバックアップ ロールバックアップ+クラスタ全体移行

▶ サンプル:pg_dumpallでクラスタ全体をバックアップ

BASH
# 全データベースとロールをバックアップ(プレーン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:

TEXT 📖 参照専用
# コマンドが正常に実行されました

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で復元

BASH
# 新しいデータベースに復元
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:

TEXT 📖 参照専用
# コマンドが正常に実行されました

▶ サンプル:選択的復元

BASH
# アーカイブ内容を一覧表示してテーブルエントリ番号を確認
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:

TEXT 📖 参照専用
# コマンドが正常に実行されました

(2) pg_restore vs psql < file.sql

観点 pg_restore psql < file.sql
入力形式 -Fc / -Fd プレーンSQLテキスト
並列処理 サポート(-j) 非サポート
選択的復元 サポート(-t / --list) 非サポート
古いデータのクリーンアップ --clean 手動でDROPを記述必要
推奨シナリオ カスタム / ディレクトリ形式 pg_dumpall出力

▶ サンプル:プレーンSQLからの復元

BASH
# 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:

TEXT 📖 参照専用
# psqlコマンドが正常に実行されました

5. 概念:COPYインポート/エクスポート

(1) COPY vs \copy

コマンド 実行場所 ファイルアクセス 必要な権限
COPY(SQL) サーバー側 サーバーファイルシステムを読み取り スーパーユーザー
\copy(psql) クライアント側 クライアントファイルを読み取り 一般ユーザー

▶ サンプル:COPYでCSVにエクスポート

SQL
-- ヘッダー付き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:

TEXT 📖 参照専用
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

▶ サンプル:COPYでCSVをインポート

SQL
-- 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:

TEXT 📖 参照専用
-- SQL文が正常に実行されました

(2) COPY形式オプション

オプション 説明
FORMAT csv / text / binary 出力形式
HEADER true / false 1行目が列名
DELIMITER ',' / '\t' 区切り文字(csvのデフォルトはカンマ)
QUOTE '"' 引用符文字
NULL '' NULLを表す文字列
ENCODING 'UTF8' ファイルエンコーディング

▶ サンプル:バイナリ形式のエクスポート/インポート

SQL
-- バイナリエクスポート(高速、小サイズ)
COPY orders TO '/tmp/orders.bin' WITH (FORMAT binary);

-- バイナリインポート
COPY orders FROM '/tmp/orders.bin' WITH (FORMAT binary);

Output:

TEXT 📖 参照専用
-- SQL文が正常に実行されました

6. 概念:WALとPITR

(1) WAL(先行書き込みログ)の原理

WALはPGがデータ整合性を保証しリカバリをサポートする中核メカニズムです。

100%
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アーカイブの設定

BASH
# 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:

TEXT 📖 参照専用
# コマンドが正常に実行されました

▶ サンプル:pg_basebackup物理ベースバックアップ

BASH
# 完全物理バックアップ(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:

TEXT 📖 参照専用
# コマンドが正常に実行されました

▶ サンプル:PITRポイントインタイムリカバリ

Bobは誤削除の1分前(午前2時59分)に復旧します:

BASH
# ステップ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:

TEXT 📖 参照専用
# コマンドが正常に実行されました

(3) 論理バックアップ vs 物理バックアップ vs PITR

観点 論理バックアップ(pg_dump) 物理バックアップ(pg_basebackup) PITR
粒度 テーブル / DB クラスタ全体 クラスタ全体
復旧精度 バックアップ時点 バックアップ時点 任意の時点
復旧速度 遅い(SQL行単位) 高速(ファイルコピー) 中程度(WAL再生)
ストレージ容量 小さい(圧縮) 大きい(完全コピー) 中程度(ベース+アーカイブ)
ゼロ損失 不可 不可
オンラインバックアップ

7. 概念:レプリケーションと高可用性

(1) ストリーミングレプリケーション vs 論理レプリケーション

観点 ストリーミングレプリケーション 論理レプリケーション
レプリケーションレベル 物理WALブロック 論理変更(INSERT/UPDATE/DELETE)
粒度 クラスタ全体 指定テーブル / パブリケーション
バージョン要件 プライマリとスタンバイのバージョンが一致必須 メジャーバージョンを跨げる
DDLレプリケーション 自動 DDLをレプリケートしない
ターゲットの書き込み可否 不可(読み取り専用スタンバイ)
PGの特長 PGネイティブ論理レプリケーション(10+)

▶ サンプル:ストリーミングレプリケーションの設定

BASH
# プライマリ側: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:

TEXT 📖 参照専用
# psqlコマンドが正常に実行されました

▶ サンプル:論理レプリケーションの設定

SQL
-- パブリッシャー側(ソースデータベース)
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:

TEXT 📖 参照専用
CREATE TABLE

(2) レプリケーション監視ビュー

ビュー 場所 内容
pg_stat_replication プライマリ 接続中の全スタンバイの状態
pg_stat_wal_receiver スタンバイ WAL受信状態
pg_stat_subscription サブスクライバー 論理レプリケーションサブスクリプション状態
pg_replication_slots プライマリ レプリケーションスロット情報

▶ サンプル:ストリーミングレプリケーション遅延の監視

SQL
-- プライマリでレプリケーション遅延を確認
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:

TEXT 📖 参照専用
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

8. 概念:バックアップ戦略

(1) フル+増分の比較

戦略 方法 復旧時間 ストレージコスト ゼロ損失
論理フル スケジュールされたpg_dump 遅い 低い 不可
物理フル スケジュールされたpg_basebackup 高速 高い 不可
PITR ベースバックアップ+WALアーカイブ 中程度 中程度
ストリーミングレプリケーション スタンバイリアルタイム同期 最速 高い ほぼゼロ損失

▶ サンプル:自動バックアップスクリプト

BASH
#!/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:

TEXT 📖 参照専用
# コマンドが正常に実行されました

(2) 推奨される本番戦略

環境 推奨ソリューション RPO RTO
開発 pg_dump日次 24時間 数時間
テスト pg_dump+ストリーミングレプリケーション 数分 数分
本番 PITR+ストリーミングレプリケーション ほぼゼロ 数分
重要本番 PITR+ストリーミングレプリケーション+論理レプリケーション ゼロ 数秒

▶ サンプル:バックアップ整合性の検証

BASH
# 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:

TEXT 📖 参照専用
# psqlコマンドが正常に実行されました

9. フローチャート:バックアップ/復元の決定

100%
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分に復旧:

SQL
-- ステップ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以上であるべき
BASH
# ステップ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';"
SQL
-- ステップ10: リカバリ成功後、新しいバックアップを取得
-- データ整合性確認後にpsqlで実行
SELECT pg_switch_wal();  -- クリーンなアーカイブポイントのためにWAL切り替えを強制
BASH
# ステップ11: 将来のPITR用に新しいベースバックアップを取得
pg_basebackup -h localhost -U postgres \
  -D /backup/base_$(date +%Y%m%d) -Ft -z -P

❓ よくある質問

Q pg_dumpはバックアップ時にテーブルをロックしますか?
A pg_dumpはスナップショット(MVCC)を使用するため、テーブルをロックせず、読み書きをブロックしません。ただし、バックアップ開始時点のデータスナップショットをバックアップするため、バックアップ中に書き込まれた新しいデータは含まれません。
Q PITRは1行が削除される直前まで復旧できますか?
A はい、WALアーカイブがその期間をカバーしていれば可能です。PITRの最も細かい粒度はトランザクションレベルで、recovery_target_xidで特定のトランザクションを指定できます。ただし、一部のトランザクションをスキップして特定の操作だけをロールバックすることはできません。
Q WALアーカイブファイルは削除できますか?
A チェックポイントを通過し、スタンバイから不要になったWALは削除できます。ただし、PITRリカバリにはベースバックアップからターゲット時刻までの全WALセグメントが必要なため、削除前に不要であることを確認してください。
Q ストリーミングレプリケーションのスタンバイは読み取りクエリを処理できますか?
A はい。hot_standby = onを設定すると、スタンバイは読み取り専用クエリ(SELECT)を受け付け、「読み取りレプリカ」と呼ばれ、分析クエリの負荷分散に適しています。
Q 論理レプリケーションはPostgreSQLのバージョンを跨げますか?
A はい。論理レプリケーションは論理変更(SQL操作)を送信するため、物理形式に依存せず、メジャーバージョンを跨いだアップグレードや移行をサポートします。これはPG論理レプリケーションの重要な利点です。
Q pg_basebackupとpg_dumpのどちらを選ぶべきですか?
A PITRやクラスタ全体のリカバリが必要な場合はpg_basebackup(物理バックアップ)を選び、選択的復元、クロスバージョン移行、単一テーブルエクスポートにはpg_dump(論理バックアップ)を選んでください。本��環境では通常両方を使用します。
Q インポートはCOPYとINSERTのどちらが速いですか?
A COPYの方が圧倒的に速いです。COPYは単一のプロトコルラウンドトリップでのバルク書き込みであり、INSERTは行ごとに解析と実行を行います。大量データのインポートにはCOPYを優先し、少量のデータにはINSERTの方が柔軟です。
Q バックアップスクリプトでのpg_restore --listのエラーは何を意味しますか?
A バックアップファイルが破損しているか不完全である可能性があります。--listは復元を実行せずにアーカイブディレクトリのみを読み取るため、バックアップ整合性を迅速に検証する方法です。破損している場合は、別のバックアップから復元してください。

📖 まとめ


📝 練習問題

  1. shop_dbデータベースをpg_dump -Fcでバックアップし、pg_restore --listでバックアップ整合性を検証し、shop_db_testデータベースに復元してください。

  2. ⭐⭐ 自動バックアップスクリプトを作成してください:日次のpg_dumpフルバックアップ(カスタム形式、圧縮)、7日間保持、バックアップ後にpg_restore --listで検証。また、ordersテーブルをCOPYでCSVにエクスポート(ヘッダー付き)し、日付で名前を付けてください。

  3. ⭐⭐⭐ 完全なPITRソリューションを設定してください:WALアーカイブを有効化(archive_commandで指定ディレクトリに設定)、pg_basebackupベースバックアップを実行、誤ってデータを削除するシミュレーション、指定時点に復旧。LSNとリカバリ時刻を記録し、データ整合性を検証してください。さらにストリーミングレプリケーションスタンバイを設定し��読み取りクエリが動作することを確認してください。

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%