PostgreSQLのエクスポート・インポートを完全マスター!データ移行もバックアップも自由自在

データベース・SQL

「本番データベースを開発環境にコピーしたい」 「PostgreSQLのバージョンアップでデータを移行したい」 「定期的なバックアップを自動化したい」 「CSVでデータをやり取りしたい」

こんな場面、よくありますよね?

PostgreSQLには強力なエクスポート・インポートツールが用意されています。この記事を読めば、データの引っ越しマスターになれます!

小規模なテーブルから、数百GBの巨大データベースまで、あらゆる場面に対応できる方法を、実例付きで解説します。


スポンサーリンク

基本を押さえる:4つのエクスポート方法

エクスポート方法の比較表

方法用途形式サイズ速度
pg_dump(SQL形式)小〜中規模、可読性重視テキスト大きい遅い
pg_dump(カスタム形式)中〜大規模、圧縮重視バイナリ小さい速い
COPY TO特定テーブルのCSV出力CSV/TSV中程度最速
pg_dumpall全データベース、全ユーザーテキスト最大遅い

それぞれ詳しく見ていきましょう!


方法1:pg_dumpでデータベースをエクスポート

基本的な使い方(SQL形式)

# データベース全体をエクスポート
pg_dump -U postgres -d mydb > mydb_backup.sql

# より詳細なオプション付き
pg_dump -U postgres -h localhost -p 5432 -d mydb -v -f mydb_backup.sql

オプションの意味:

  • -U: ユーザー名
  • -h: ホスト名
  • -p: ポート番号
  • -d: データベース名
  • -v: 詳細表示(verbose)
  • -f: 出力ファイル名

スキーマのみエクスポート(構造だけ)

# テーブル構造、インデックス、制約のみ
pg_dump -U postgres -d mydb --schema-only > schema.sql

# 実行例の出力内容
-- CREATE TABLE employees (
--     id SERIAL PRIMARY KEY,
--     name VARCHAR(100),
--     salary INTEGER
-- );
-- CREATE INDEX idx_employees_name ON employees(name);

データのみエクスポート(構造なし)

# INSERTステートメントとして出力
pg_dump -U postgres -d mydb --data-only > data.sql

# COPYコマンド形式で出力(高速)
pg_dump -U postgres -d mydb --data-only --column-inserts > data_inserts.sql

特定テーブルのみエクスポート

# 単一テーブル
pg_dump -U postgres -d mydb -t employees > employees.sql

# 複数テーブル
pg_dump -U postgres -d mydb -t employees -t departments > tables.sql

# ワイルドカード使用
pg_dump -U postgres -d mydb -t 'public.emp*' > emp_tables.sql

# 特定テーブルを除外
pg_dump -U postgres -d mydb -T logs -T temp_* > backup_without_logs.sql

方法2:カスタム形式で圧縮エクスポート(推奨!)

なぜカスタム形式が推奨なのか?

メリット:

  • ✅ ファイルサイズが1/5〜1/10に圧縮
  • ✅ 並列リストアが可能
  • ✅ 部分的なリストアが可能
  • ✅ エラー時の再開が可能
# カスタム形式でエクスポート(-Fc)
pg_dump -U postgres -d mydb -Fc -f mydb.dump

# 圧縮レベル指定(0-9、デフォルト6)
pg_dump -U postgres -d mydb -Fc -Z 9 -f mydb_max_compress.dump

# 並列処理でエクスポート(大規模DB向け)
pg_dump -U postgres -d mydb -Fc -j 4 -f mydb_parallel.dump

ディレクトリ形式(超大規模向け)

# ディレクトリ形式でエクスポート
pg_dump -U postgres -d mydb -Fd -f mydb_dir/

# 並列処理付き(各テーブルが別ファイルになる)
pg_dump -U postgres -d mydb -Fd -j 8 -f mydb_dir/

方法3:COPYコマンドでCSVエクスポート

テーブルをCSVファイルへ

-- PostgreSQL内から実行(サーバー側のファイル)
COPY employees TO '/tmp/employees.csv' WITH (FORMAT CSV, HEADER);

-- より詳細な設定
COPY employees TO '/tmp/employees.csv' 
WITH (
    FORMAT CSV,
    HEADER true,
    DELIMITER ',',
    QUOTE '"',
    ESCAPE '"',
    NULL 'NULL'
);

psqlから実行(クライアント側)

# \copyコマンドを使用(クライアント側のファイルに出力)
psql -U postgres -d mydb -c "\copy employees TO 'employees.csv' WITH (FORMAT CSV, HEADER)"

# 条件付きエクスポート
psql -U postgres -d mydb -c "\copy (SELECT * FROM employees WHERE department = '営業部') TO 'sales_employees.csv' CSV HEADER"

プログラムから実行(スクリプト例)

#!/bin/bash
# export_tables.sh - 全テーブルをCSVエクスポート

DB="mydb"
USER="postgres"
OUTPUT_DIR="./csv_export"

mkdir -p $OUTPUT_DIR

# テーブル一覧を取得
TABLES=$(psql -U $USER -d $DB -t -c "SELECT tablename FROM pg_tables WHERE schemaname='public'")

# 各テーブルをCSVエクスポート
for TABLE in $TABLES; do
    echo "Exporting $TABLE..."
    psql -U $USER -d $DB -c "\copy $TABLE TO '$OUTPUT_DIR/$TABLE.csv' CSV HEADER"
done

インポート:データを取り込む

SQL形式のインポート

# 基本的なインポート
psql -U postgres -d mydb < mydb_backup.sql

# エラーで停止しない
psql -U postgres -d mydb -f mydb_backup.sql --single-transaction

# 進捗を表示
psql -U postgres -d mydb -f mydb_backup.sql -v ON_ERROR_STOP=1

カスタム形式のインポート(pg_restore)

# 基本的なリストア
pg_restore -U postgres -d mydb mydb.dump

# データベースを作成してからリストア
pg_restore -U postgres -C -d postgres mydb.dump

# 並列リストア(高速)
pg_restore -U postgres -d mydb -j 4 mydb.dump

# データのみリストア(構造は既存)
pg_restore -U postgres -d mydb --data-only mydb.dump

# 特定テーブルのみリストア
pg_restore -U postgres -d mydb -t employees mydb.dump

CSVファイルのインポート

-- COPY FROM(サーバー側のファイル)
COPY employees FROM '/tmp/employees.csv' WITH (FORMAT CSV, HEADER);

-- データを追記ではなく置換したい場合
TRUNCATE employees;
COPY employees FROM '/tmp/employees.csv' WITH (FORMAT CSV, HEADER);
# psqlから(クライアント側のファイル)
psql -U postgres -d mydb -c "\copy employees FROM 'employees.csv' CSV HEADER"

実践例:よくあるシナリオ

シナリオ1:本番DBを開発環境にコピー

#!/bin/bash
# production_to_dev.sh

# 1. 本番DBをエクスポート
echo "本番DBをエクスポート中..."
pg_dump -h prod-server -U postgres -d production_db -Fc -f prod_backup.dump

# 2. 開発DBを削除・再作成
echo "開発DBをリセット中..."
dropdb -h dev-server -U postgres dev_db
createdb -h dev-server -U postgres dev_db

# 3. データをインポート
echo "開発環境にインポート中..."
pg_restore -h dev-server -U postgres -d dev_db -j 4 prod_backup.dump

# 4. 個人情報をマスキング(オプション)
echo "個人情報をマスキング中..."
psql -h dev-server -U postgres -d dev_db << EOF
UPDATE users SET 
    email = 'user' || id || '@example.com',
    phone = '090-0000-' || LPAD(id::text, 4, '0');
EOF

echo "完了!"

シナリオ2:定期バックアップスクリプト

#!/bin/bash
# daily_backup.sh

# 設定
DB_NAME="mydb"
DB_USER="postgres"
BACKUP_DIR="/backup/postgresql"
DATE=$(date +%Y%m%d_%H%M%S)
KEEP_DAYS=7

# バックアップ実行
echo "バックアップ開始: $DATE"

# フルバックアップ(カスタム形式)
pg_dump -U $DB_USER -d $DB_NAME -Fc -f "$BACKUP_DIR/full_${DB_NAME}_${DATE}.dump"

# スキーマのみ(テキスト形式で保存)
pg_dump -U $DB_USER -d $DB_NAME --schema-only -f "$BACKUP_DIR/schema_${DB_NAME}_${DATE}.sql"

# 古いバックアップを削除
find $BACKUP_DIR -name "*.dump" -mtime +$KEEP_DAYS -delete
find $BACKUP_DIR -name "*.sql" -mtime +$KEEP_DAYS -delete

echo "バックアップ完了: $DATE"

# crontabに登録して毎日2時に実行
# 0 2 * * * /path/to/daily_backup.sh

シナリオ3:大規模DBの移行(100GB以上)

#!/bin/bash
# large_db_migration.sh

# 並列処理とディレクトリ形式を使用
SOURCE_DB="large_production_db"
TARGET_DB="new_production_db"
JOBS=8  # CPU数に応じて調整

# 1. エクスポート(並列・ディレクトリ形式)
echo "大規模DBをエクスポート中..."
time pg_dump -U postgres -d $SOURCE_DB -Fd -j $JOBS -f db_export_dir/

# 2. 転送(必要に応じて)
echo "ファイルを転送中..."
rsync -avz --progress db_export_dir/ remote-server:/tmp/db_export_dir/

# 3. インポート(並列)
echo "新環境にインポート中..."
time pg_restore -U postgres -d $TARGET_DB -j $JOBS db_export_dir/

# 4. 統計情報を更新
echo "統計情報を更新中..."
psql -U postgres -d $TARGET_DB -c "ANALYZE;"

トラブルシューティング

エラー1:権限不足

# エラー: permission denied for schema public

# 解決策1: スーパーユーザーでエクスポート/インポート
pg_dump -U postgres -d mydb > backup.sql

# 解決策2: --no-owner オプションを使用
pg_dump -U user -d mydb --no-owner --no-acl > backup.sql

エラー2:エンコーディングの不一致

# エラー: ERROR: invalid byte sequence for encoding "UTF8"

# 解決策: エンコーディングを指定
pg_dump -U postgres -d mydb -E UTF8 > backup.sql

# インポート時に変換
iconv -f LATIN1 -t UTF8 backup.sql | psql -U postgres -d mydb

エラー3:メモリ不足

# 大規模テーブルでメモリ不足

# 解決策1: COPYを使用
psql -c "\copy large_table TO 'large_table.csv' CSV"

# 解決策2: WHERE句で分割
pg_dump -U postgres -d mydb -t large_table --where="id < 1000000" > part1.sql
pg_dump -U postgres -d mydb -t large_table --where="id >= 1000000" > part2.sql

パフォーマンスのヒント

エクスポートの高速化

# 1. 並列処理を使用
pg_dump -Fd -j 8 -f backup_dir/

# 2. 圧縮を無効化(ネットワークが速い場合)
pg_dump -Fc -Z 0 -f backup.dump

# 3. --exclude-table-dataで不要なデータを除外
pg_dump --exclude-table-data='logs*' -f backup.sql

インポートの高速化

-- インポート前の設定変更
ALTER SYSTEM SET maintenance_work_mem = '2GB';
ALTER SYSTEM SET max_wal_size = '4GB';
ALTER SYSTEM SET checkpoint_completion_target = 0.9;
ALTER SYSTEM SET wal_buffers = '16MB';

-- 自動VACUUM無効化
ALTER SYSTEM SET autovacuum = off;

-- インポート後に戻す
ALTER SYSTEM RESET ALL;
SELECT pg_reload_conf();

セキュリティの考慮事項

パスワードの扱い

# 方法1: .pgpassファイルを使用
echo "localhost:5432:mydb:postgres:mypassword" > ~/.pgpass
chmod 600 ~/.pgpass

# 方法2: 環境変数を使用
export PGPASSWORD='mypassword'
pg_dump -U postgres -d mydb > backup.sql

# 方法3: 接続文字列を使用(URLエンコード注意)
pg_dump "postgresql://user:pass@localhost/mydb" > backup.sql

バックアップファイルの暗号化

# エクスポート時に暗号化
pg_dump -U postgres -d mydb | gzip | openssl enc -aes-256-cbc -out backup.sql.gz.enc

# 復号化してインポート
openssl enc -d -aes-256-cbc -in backup.sql.gz.enc | gunzip | psql -U postgres -d mydb

まとめ:もうデータ移行で悩まない!

今日マスターした重要ポイント:

pg_dump = 最も基本的で万能なツール ✅ カスタム形式(-Fc) = 圧縮+高速+柔軟 ✅ 並列処理(-j) = 大規模DBの救世主 ✅ COPY = CSVでのやり取りに最適 ✅ pg_restore = カスタム形式の相棒 ✅ –schema-only/–data-only = 構造とデータを分離 ✅ 定期バックアップ = スクリプト化で自動化

最初は「コマンドが多くて覚えられない…」と感じるかもしれません。

でも、基本はpg_dump -Fcでエクスポート、pg_restoreでインポート。これだけ覚えれば、ほとんどの場面で対応できます。

今日から、データの引っ越しは怖くありません!

Happy Migration!


次のステップにおすすめ:

  • PostgreSQLレプリケーションでリアルタイム同期
  • pg_basebackupで物理バックアップ
  • WALアーカイブでポイントインタイムリカバリ

コメント

タイトルとURLをコピーしました