背景と導入理由:本番環境における論理ダンプの限界
エンジニアとしてのキャリアの初期は、単純な mysqldump の cron ジョブを実行するだけで十分だと考えていました。しかし、主要な本番データベースの容量が 150 GB を超えた瞬間、状況は一変しました。毎夜のダンプによって深刻なクエリスパイクが発生し、InnoDB バッファプールが汚染され、バッチ処理のピーク時にはテーブルロックが発生するようになったのです。さらに深刻だったのは、ディザスタリカバリ訓練でその単一のダンプファイルをリストアするのに 6 時間以上もかかったことでした。
高スループットが求められる MySQL ワークロードにおいて、論理ダンプはスケールしません。論理バックアップは何百万もの SQL INSERT 文を使ってデータセット全体を 1 行ずつ再構築します。これに対し、物理バックアップはストレージ層で直接動作し、生のデータファイル、テーブルスペース、インデックスをブロックレベルでコピーします。
Percona XtraBackup は、完全なノンブロッキングのホットバックアップを実行することで、ダウンタイムの問題を解決します。データベースが毎秒数千件のアクティブなクエリを処理している最中でも、XtraBackup は InnoDB のデータページをコピーし、redo ログからの変更をリアルタイムで追跡・収集します。これにより、テーブルをロックしたりユーザーのトラフィックを低下させたりすることなく、ポイントインタイムでクラッシュ整合性のあるスナップショットが保証されます。
インストールとリポジトリの設定
MySQL 8.0 環境では、Percona XtraBackup 8.0 をインストールします。メジャーバージョンを一致させる必要がある点に注意してください。XtraBackup 8.0 で MySQL 5.7 をバックアップすることはできず、XtraBackup 2.4 で MySQL 8.0 をバックアップすることもできません。
1. Percona リポジトリのインストール
Debian および Ubuntu システムでは、公式の Percona リリースパッケージを登録します:
# Percona リポジトリのダウンロードと登録
wget https://repo.percona.com/apt/percona-release_latest.generic_all.deb
sudo dpkg -i percona-release_latest.generic_all.deb
# XtraBackup 8.0 用の tools リポジトリを有効化
sudo percona-release enable-only tools release
sudo apt-get update
# パッケージのインストール
sudo apt-get install -y percona-xtrabackup-80
RHEL、Rocky Linux、または AlmaLinux システムの場合:
sudo yum install -y https://repo.percona.com/yum/percona-release-latest.noarch.rpm
sudo percona-release enable-only tools release
sudo yum install -y percona-xtrabackup-80
2. 専用バックアップユーザーの作成
自動バックアップを MySQL の root スーパーユーザーで実行してはいけません。データページの読み取り、レプリケーション状態の検査、ログのフラッシュに必要な最小限の権限を持つ専用アカウントを作成してください:
sudo mysql -u root -p
CREATE USER 'xtrabackup'@'localhost' IDENTIFIED BY 'StrongBackupPass123!';
GRANT BACKUP_ADMIN, PROCESS, RELOAD, LOCK TABLES, REPLICATION CLIENT
ON *.* TO 'xtrabackup'@'localhost';
FLUSH PRIVILEGES;
EXIT;
設定:フルバックアップおよび増分バックアップパイプラインの構築
堅牢なバックアップ戦略では、通常、週 1 回のフルベースバックアップと日次の増分バックアップを組み合わせます。増分バックアップでは、前回のバックアップ以降に変更されたデータページのみがコピーされます。日々の変更が 5 GB 程度である 200 GB のデータベースの場合、このアプローチによりバックアップウィンドウを 40 分から 3 分未満にまで劇的に短縮できます。
1. 保存先ディレクトリのセットアップ
OS ドライブの容量枯渇を防ぐため、バックアップディレクトリは専用ディスクまたはネットワークマウントポイント上に配置します:
sudo mkdir -p /var/backups/mysql/{full,inc}
sudo chown -R mysql:mysql /var/backups/mysql
sudo chmod -R 700 /var/backups/mysql
2. 手動でのフルバックアップの実行
権限とベースラインのパフォーマンスをテストするために、初回フルバックアップを実行します:
xtrabackup --backup \
--user=xtrabackup \
--password='StrongBackupPass123!' \
--target-dir=/var/backups/mysql/full/base_$(date +%F)
コマンドが完了したら、出力の最終行に completed OK! が表示されていることを確認してください。ターゲットフォルダには、生のデータファイルとともに、ログシーケンス番号(LSN)のチェックポイントを保持する xtrabackup_checkpoints というメタデータファイルが生成されます。
3. 増分バックアップの実行
増分バックアップはベースバックアップと対象ディレクトリを比較し、前回のチェックポイントよりも高い LSN を持つページのみをコピーします:
xtrabackup --backup \
--user=xtrabackup \
--password='StrongBackupPass123!' \
--target-dir=/var/backups/mysql/inc/inc_$(date +%F) \
--incremental-basedir=/var/backups/mysql/full/base_2026-10-01
4. 本番環境向け自動化スクリプト
本番環境には信頼性の高い自動化が不可欠です。以下の Bash スクリプトは、毎週日曜日にフルバックアップを実行し、月曜日から土曜日には増分バックアップを実行します。また、ディレクトリのシンボリックリンクを管理し、14 日以上経過したバックアップを削除します。
このスクリプトを /usr/local/bin/mysql-backup.sh に保存します:
#!/usr/bin/env bash
set -euo pipefail
BACKUP_ROOT="/var/backups/mysql"
FULL_DIR="${BACKUP_ROOT}/full"
INC_DIR="${BACKUP_ROOT}/inc"
USER="xtrabackup"
PASS="StrongBackupPass123!"
TODAY=$(date +%Y-%m-%d)
DAY_OF_WEEK=$(date +%u) # 7 = 日曜日
mkdir -p "${FULL_DIR}" "${INC_DIR}"
if [ "$DAY_OF_WEEK" -eq 7 ] || [ ! -d "${FULL_DIR}/latest" ]; then
echo "[$(date)] フルバックアップを開始します..."
TARGET="${FULL_DIR}/full_${TODAY}"
xtrabackup --backup \
--user="${USER}" \
--password="${PASS}" \
--target-dir="${TARGET}"
ln -sfn "${TARGET}" "${FULL_DIR}/latest"
rm -rf "${INC_DIR}"/*
echo "[$(date)] フルバックアップが完了しました: ${TARGET}"
else
echo "[$(date)] 増分バックアップを開始します..."
LAST_INC=$(find "${INC_DIR}" -maxdepth 1 -mindepth 1 -type d | sort | tail -n 1)
if [ -z "${LAST_INC}" ]; then
BASE_DIR="${FULL_DIR}/latest"
else
BASE_DIR="${LAST_INC}"
fi
TARGET="${INC_DIR}/inc_${TODAY}"
xtrabackup --backup \
--user="${USER}" \
--password="${PASS}" \
--target-dir="${TARGET}" \
--incremental-basedir="${BASE_DIR}"
echo "[$(date)] ベース ${BASE_DIR} に対する増分バックアップが完了しました: ${TARGET}"
fi
# 14日以上経過したフルバックアップを削除
find "${FULL_DIR}" -maxdepth 1 -mindepth 1 -type d -mtime +14 -exec rm -rf {} +
実行権限を付与し、crontab にジョブを登録します:
sudo chmod +x /usr/local/bin/mysql-backup.sh
# 毎晩深夜 02:00 に実行するようスケジュール設定
(crontab -l 2>/dev/null; echo "0 2 * * * /usr/local/bin/mysql-backup.sh >> /var/log/mysql-backup.log 2>&1") | crontab -
検証とリカバリ:障害発生前にリストアをテストする
テストされていないバックアップはバックアップではなく、ただの「願望」にすぎません。物理バックアップはトランザクション実行中のライブデータページをキャプチャするため、ファイル内には未コミットの変更が含まれています。MySQL で安全に使用できるようにするには、バックアップを準備(prepare)する必要があります。
1. バックアップチェーンの準備(Prepare)
増分変更を含むベースバックアップをリストアするには、各増分フォルダからコミット済みトランザクションをベースディレクトリへロールフォワードします。以降の差分適用に備えてベースバックアップを開いた状態にしておくため、中間ステップではすべて --apply-log-only フラグを使用します:
# ステップ 1: フルベースバックアップを準備(増分適用のためにログを開いたままにする)
xtrabackup --prepare \
--apply-log-only \
--target-dir=/var/backups/mysql/full/full_2026-10-01
# ステップ 2: 増分バックアップをベースディレクトリに適用
xtrabackup --prepare \
--apply-log-only \
--target-dir=/var/backups/mysql/full/full_2026-10-01 \
--incremental-dir=/var/backups/mysql/inc/inc_2026-10-02
# ステップ 3: ベースに対する最終 prepare(未コミットのトランザクションをロールバック)
xtrabackup --prepare \
--target-dir=/var/backups/mysql/full/full_2026-10-01
2. MySQL データディレクトリへのファイルリストア
ベースバックアップの prepare が完全に完了したら、生のデータファイルを MySQL ディレクトリにコピーして戻します:
# データベースサービスを停止
sudo systemctl stop mysql
# 現在のデータディレクトリをバックアップし、空のターゲットディレクトリを再作成
sudo mv /var/lib/mysql /var/lib/mysql.bak_$(date +%F)
sudo mkdir -p /var/lib/mysql
# 準備済みデータファイルのリストア
xtrabackup --copy-back \
--target-dir=/var/backups/mysql/full/full_2026-10-01
# ファイルの所有権と権限を修正
sudo chown -R mysql:mysql /var/lib/mysql
sudo chmod 750 /var/lib/mysql
# MySQL サービスを起動しステータスを確認
sudo systemctl start mysql
sudo systemctl status mysql
3. 監視とヘルスチェック
以下の 4 つの運用チェックを監視スタック(Prometheus node-exporter、Datadog、Zabbix など)に統合してください:
- プロセスの終了コード:
xtrabackupコマンドが0以外の終了コードを返した場合は、直ちにアラートを発報します。 - ログ文字列の検証:
/var/log/mysql-backup.logをパースし、completed OK!という確認文字列が存在することを検証します。 - LSN の連続性チェック:
xtrabackup_checkpointsを確認し、各増分バックアップのfrom_lsnが直前のバックアップのto_lsnと一致していることを検証します。 - サンドボックスへの自動リストア: 週次で CI/CD ランナーをスケジュールし、隔離されたステージングインスタンスに最新のバックアップチェーンをリストアして重要テーブルに対する検証クエリを実行します。

