データベースのバックアップ・診断マニュアル

9 分で読了
このページの目次

engine・instance・DB 名・保存先を先に確認する。ファイルの存在は復元成功の証拠ではない。一貫性・範囲・隔離復元を一連で扱う。アプリ内部表は対応する公式手段で管理する。

やりたいこと参照先
SQLite の backupWAL と online backup
PostgreSQL の dump/restoreDB と global object
MySQL / MariaDBsnapshot の前提
自動 backup の検証復元演習と保持

稼働中の main DB だけをコピーしない

DB と構造を読取専用で確認する

# 対象の SQLite ファイルを確認する
sqlite3 -readonly /var/lib/app/app.db '.databases'

main の実パスを確認する。-readonly は誤った場所での空 DB 作成を防ぐ。以後の元パスを揃える。

# journal mode と backup 範囲を確認する
sqlite3 -readonly /var/lib/app/app.db 'PRAGMA journal_mode;'

wal なら commit 済みデータが WAL にある場合がある。主ファイルだけコピーしたり WAL を消したりしない。

# 実際の table 名を調べる
sqlite3 -readonly /var/lib/app/app.db '.tables'

実名で .schema TABLE_NAME を別途読む。dot command は CLI 専用で、SQL driver に送らない。

path を置換する。-readonly は誤記で空 DB を作るのを防ぐ。dot command は SQL ではない。WAL の commit 済み内容は main に未反映の場合がある。稼働中の -wal/-shm を消さない。

新しい制限付き directory に整合した副本を作る

# 今回専用の未使用 directory を制限付きで作る
umask 077
mkdir -m 700 ./db-review

管理された場所で実行する。既存なら新名へ変え、後続パスも合わせる。umask はこの shell の新規ファイルに作用する。

# SQLite backup API で稼働 DB から新副本を作る
sqlite3 -readonly /var/lib/app/app.db <<'SQLITE'
.bail on
.timeout 5000
.backup './db-review/app-backup.db'
SQLITE

heredoc 内は CLI 命令。.bail はエラー終了、.timeout は lock 待機ミリ秒。終了コードを確認し、失敗副本を成功扱いしない。読取専用の元でも backup 先には書ける。

# 副本の構造整合性を検査する
sqlite3 -readonly ./db-review/app-backup.db 'PRAGMA integrity_check;'

正常は ok。エラーを保存し、存在だけで成功としない。

# 定義済み外部 key の違反を調べる
sqlite3 -readonly ./db-review/app-backup.db 'PRAGMA foreign_key_check;'

出力なしは定義済み制約の違反なし。業務関係全体ではない。代表レコード・時刻と隔離環境のアプリ起動を確認する。

新しい保存先を使い唯一の検証済み copy を上書きしない。.backup も lock 競合し得るため終了 status を見る。integrity_check は ok、foreign_key_check は違反なしを期待するが業務整合性は別検証。

単一 DB dump は cluster 全体ではない

PostgreSQL:接続設定を先に確認する

service=app と保護された資格情報を用意し、restore は別の隔離 instance とする。今回専用の制限付き directory に出力し、旧 backup を上書きしない。

# 元 DB・user・server version を確認する
psql 'service=app' -X -c 'SELECT current_database(), current_user, version();'

-X は psqlrc を読まない。特に古い major の pg_dump で新しい server を export しないよう対応を確認する。

# 一つの DB を選択復元可能な custom archive に出す
pg_dump --dbname='service=app' --format=custom --file=app.dump

終了コードとサイズを確認する。custom は pg_restore 用で、psql へリダイレクトしない。lock/DDL 診断を読む。

# role・tablespace など global object を別に出す
pg_dumpall --database='service=app' --globals-only --file=globals.sql

読取権限が必要で機密 role 情報を含み得る。隔離環境へ自動投入せず、role 衝突・パス・権限を確認する。

# archive の object 一覧を確認する
pg_restore --list app.dump

必要な schema・table・sequence・function を確認する。一覧表示できても復元成功ではない。

service=app を事前設定し pgpass 等を保護する(Unix は0600)。pg_dump は通常 DML と並行可能だが DDL/lock と競合する。role・tablespace・server 設定・連続 WAL 復旧は単庫 dump に含まれない。globals も保護する。

隔離 instance に新しい復元 DB を作る

# 書込前に復元 instance の識別を再確認する
psql 'service=restore' -X -c 'SELECT inet_server_addr(), inet_server_port(), current_database(), current_user;'

host/socket 設定を確認し、本番でないことを確かめる。Unix socket では inet_server_addr が NULL の場合がある。

# 隔離 instance に未存在の空 DB を作る
createdb --maintenance-db='service=restore' app_restore

既存なら削除せず確認する。後続も app_restore に揃える。

# 新 DB へ復元し、エラーで止める
pg_restore --dbname='service=restore dbname=app_restore' \
  --exit-on-error --no-owner --no-privileges app.dump

元 ownership/ACL を意図的に省く。正式復旧は別途設計する。失敗は部分 object を残し得るため、log を保存し新しい演練 DB で再試行する。

# 検証先が復元 DB であることを確認する
psql 'service=restore dbname=app_restore' -X -c 'SELECT current_database();'

続いて業務の読取 query・制約・extension・sequence・互換性を確認し、所要時間を記録する。DB 名確認だけでは完了でない。

性能診断は接続停止の前に待機を見る

# 長い transaction と待機種別を読む
psql 'service=app' -X -c "SELECT pid, state, wait_event_type, wait_event, xact_start, query_start FROM pg_stat_activity WHERE datname = current_database() ORDER BY xact_start NULLS LAST;"

state・開始時刻・待機を合わせる。query cancel と session terminate は影響が違うため、PID と目的を確認して別途判断する。

restore は隔離試験 instance。例は復元 user に所有させ元の権限を戻さない。本復旧では role・所有・ACL・extension・version を計画する。live DB へ --clean を使わず、性能問題は pg_stat_activity の wait と長期 transaction を見る。

single-transaction は engine が前提

MySQL:元と復元先の login-path を用意する

Oracle MySQL client の例。mysql_config_editor で別 instance の app/restore を設定し、password は対話入力する。MariaDB は対応 client と保護 option file を使う。

# 元 DB・認証主体・版を確認する
mysql --login-path=app --database=app -e 'SELECT DATABASE(), CURRENT_USER(), VERSION();'

--database で元を明示し、DATABASE() が NULL のまま対象確定としない。

# table engine が transaction snapshot の前提を満たすか調べる
mysql --login-path=app -e "SELECT TABLE_NAME, ENGINE FROM information_schema.TABLES WHERE TABLE_SCHEMA='app' AND TABLE_TYPE='BASE TABLE';"

MyISAM 等があれば別の整合性確保、保守 lock・物理 backup などが必要。

# 独立復元用に transaction DB と program object を出す
mysqldump --login-path=app --single-transaction \
  --routines --events --triggers --set-gtid-purged=OFF app > app.sql

制限付き directory で終了成功を確認する。GTID OFF は独立演練用で、replication 復旧にはそのまま使わない。DDL・DEFINER・権限・文字集合を確認する。

別 instance を確認してから復元する

# 隔離復元先を確認する
mysql --login-path=restore -e 'SELECT @@hostname, @@port, CURRENT_USER(), VERSION();'

login-path の host/socket も照合する。hostname だけでは隔離の証明にならない。

# 既存同名 DB を上書きせず新規作成する
mysql --login-path=restore -e 'CREATE DATABASE app_restore;'

元 charset/collation に合わせる。同名既存なら止めて調べる。

# 確認した SQL を隔離 DB にだけ取り込む
mysql --login-path=restore app_restore < app.sql

終了・log と table・data・routine・event・アプリを確認する。USE/CREATE/DEFINER 等の範囲・権限変更を事前確認し、--force で無視しない。

login-path を事前設定する。MariaDB は対応 client/dump と保護した option file を使う。single-transaction は InnoDB 等向けで MyISAM の整合性は保証しない。DDL を避け、routine・event・trigger・文字・権限・GTID・版を確認する。

独立環境への復元で backup を検証

backup の検証連鎖 整合した backup 成功 別環境へ復元 構造・data 検証 アプリ動作確認
自動化で残す情報意味
開始/終了・終了値・容量・checksum失敗・空 file・転送破損を検出。復元の代替ではない
instance・DB・版・対象対象違いや role/設定漏れを防ぐ
復元時間と業務確認復旧時間と復旧点を評価

systemd timer は一意の一時 file に書き、成功後に同じ FS で rename して独立 copy を保持する。失敗で前の正常版を失わず、保持整理は検証後にする。Grafana は設定・plugin・暗号鍵も必要で内部表を直接編集しない。

SQLite backup · SQLite CLI · PostgreSQL dumps · MySQL mysqldump