記事の一覧

MySQL のバックアップの取り方 mysqldump から RDS スナップショットまで

MySQL のバックアップ方法を、mysqldump、mysqlpump、Percona XtraBackup、バイナリログ、RDS・Aurora のスナップショットの順に、使いどころと手順を説明します。リストアの手順と、バックアップが動いているかの確認方法もまとめました。

MySQL のバックアップは、データの大きさや環境によって向いている方法が変わります。

MySQL のバックアップとは

障害や操作ミスでデータを失ったときに、ある時点の状態へ戻せるようにデータの写しを取っておくことです。大きく二つに分かれます。

論理バックアップはテーブルの定義とデータを SQL 文として書き出す方法です。mysqldump がこれにあたります。テキストなので扱いやすい反面、データが大きいと書き出しにも戻すのにも時間がかかります。

物理バックアップはデータファイルそのものをコピーする方法です。Percona XtraBackup や RDS のスナップショットがこれにあたります。大きなデータでも速く取れて速く戻せますが、取った環境と近い構成にしか戻せません。

方式 代表的な方法 向いている規模 戻す速さ
論理 mysqldump、mysqlpump 小〜中規模 (数十 GB まで) 遅い
物理 XtraBackup、スナップショット 中〜大規模 速い
差分 (ログ) バイナリログ 全体バックアップとの併用 時点を指定して戻せる

以降のコマンドはデモ用のデータベース demo_shop を例にしています。DB 名、ユーザー名、保存先は自分の環境に読み替えてください。

1. mysqldump

MySQL に標準で付いている論理バックアップのコマンドです。DB 全体、特定のデータベース、特定のテーブルといった単位で CREATE TABLE 文と INSERT 文のファイルを作ります。別バージョンの MySQL や別のサーバーにも戻しやすく、中身をテキストで確認できます。

データが数十 GB までで、バックアップに数分から数十分かかっても構わない場面で使います。開発環境へのデータの複製や、バージョンアップ前の保険にも向いています。

InnoDB のテーブルが中心なら --single-transaction を付けるのが基本です。

mysqldump \
  --single-transaction \
  --routines --triggers --events \
  --source-data=2 \
  --default-character-set=utf8mb4 \
  -u backup_user -p \
  demo_shop | gzip > demo_shop_$(date +%Y%m%d_%H%M).sql.gz

主なオプションの意味は次のとおりです。

  • --single-transaction: ダンプの間、一貫した読み取りのトランザクションを開き、テーブルをロックせずに同じ時点のデータを書き出す。InnoDB のみ有効
  • --routines --triggers --events: ストアドプロシージャ、トリガー、イベントも含める。トリガーは標準で含まれるが、プロシージャとイベントは指定しないと抜け落ちる
  • --source-data=2: ダンプの先頭にバイナリログの位置をコメントで残す。ポイントインタイムリカバリに使う (MySQL 8.0.26 より前は --master-data=2)
  • --default-character-set=utf8mb4: 書き出しに使う文字コードを明示する

パスワードをコマンドラインに直接書くと ps コマンドや履歴に残ります。cron で動かすときは権限を 600 にした設定ファイルを使います。

# /etc/mysql/backup.cnf (chmod 600)
[client]
user=backup_user
password=ここにパスワード
mysqldump --defaults-extra-file=/etc/mysql/backup.cnf --single-transaction demo_shop

バックアップ用のユーザーには全権限ではなく必要な分だけを与えます。SELECT、SHOW VIEW、TRIGGER、EVENT、LOCK TABLES あたりです。--single-transaction と --source-data を使うなら、環境によって RELOAD や PROCESS も要ります。

リストア

# データベースを作ってから戻す
mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS demo_shop DEFAULT CHARACTER SET utf8mb4"
gunzip < demo_shop_20261108_0300.sql.gz | mysql -u root -p --default-character-set=utf8mb4 demo_shop

特定のテーブルだけ戻したいときは、そのテーブルだけをダンプしておくか、ダンプファイルから該当部分を切り出します。大きなファイルでは pv コマンドを間に挟むと進み具合が分かります。

気をつけること

  • MyISAM のテーブルには --single-transaction が効かず、ダンプ中にテーブルがロックされる。MyISAM が混ざっているなら本番ではなくレプリカから取る
  • ダンプ中は長い読み取りトランザクションが開きっぱなしになり、InnoDB の履歴領域 (undo) が膨らむ。書き込みが多い DB では実行中の負荷にも気をつける
  • 戻すときは INSERT を流し込むため、数百 GB のデータでは何時間もかかる

2. mysqlpump

mysqlpump は mysqldump を並列にした論理バックアップのコマンドです。複数のスレッドでテーブルをまとめて書き出せるため、テーブル数が多い DB では mysqldump より速く取れます。

mysqlpump --default-parallelism=4 --exclude-databases=mysql,sys -u backup_user -p demo_shop > demo_shop.sql

MySQL 8.0.34 で非推奨になり、MySQL 9.0 では削除されています。これから新しく採用する理由はありません。すでに使っているなら移行先を決めておきましょう。

  • 並列で速くしたいなら、MySQL Shell のダンプ機能 (util.dumpInstance()、util.dumpSchemas()) と util.loadDump() が後継として案内されている
  • 物理バックアップに移れるなら、次の XtraBackup が選択肢になる
  • 並列が要らないなら、素直に mysqldump に戻せる

3. Percona XtraBackup

Percona XtraBackup はデータファイルを直接コピーする物理バックアップのツールです。サーバーを止めずに取れる「ホットバックアップ」ができ、書き込みを止めずに InnoDB のデータを一貫した状態でコピーします。前回からの変更分だけを取る増分バックアップにも対応しています。MySQL 本体には含まれない、Percona 社のオープンソースのツールです。

数百 GB 以上でダンプでは時間がかかりすぎる場合や、障害から短時間で復旧したい場合に向いています。対象は InnoDB で、MyISAM のテーブルはコピー中にロックがかかります。MySQL 8.0 に対応するのは XtraBackup 8.0 系です。

バックアップと増分

# フルバックアップ
xtrabackup --backup --user=backup_user --password \
  --target-dir=/backup/full_20261108

# 前回からの増分
xtrabackup --backup --user=backup_user --password \
  --target-dir=/backup/inc_20261109 \
  --incremental-basedir=/backup/full_20261108

リストア

物理バックアップは取った直後のままでは使えません。コピー中に変わったデータをログから反映して整合させる --prepare が必要です。

# 1. 整合させる
xtrabackup --prepare --target-dir=/backup/full_20261108

# 2. MySQL を止め、データディレクトリを退避して空にする
sudo systemctl stop mysqld
sudo mv /var/lib/mysql /var/lib/mysql.old
sudo mkdir /var/lib/mysql

# 3. 戻す
xtrabackup --copy-back --target-dir=/backup/full_20261108
sudo chown -R mysql:mysql /var/lib/mysql

# 4. 起動する
sudo systemctl start mysqld

増分を使うときは、フルに対して --prepare --apply-log-only で増分を順に重ねてから、最後に通常の --prepare をかけます。

運用上の制約

  • 物理バックアップは MySQL のバージョンと設定が近い環境にしか戻せない。取る側と戻す側のバージョンを揃える
  • 戻す手順を間違えると元のデータを失うため、/var/lib/mysql は消さずに退避しておく
  • バックアップのファイルはデータそのもの。暗号化と保存先のアクセス制限を忘れない
  • RDS や Aurora ではサーバー側のファイルに触れないため XtraBackup で取ることはできない (RDS for MySQL には S3 に置いた XtraBackup の結果を取り込む機能があるが、これは移行用)

4. バイナリログによるポイントインタイムリカバリ

バイナリログは、MySQL が実行したデータの変更を順に記録するファイルです。夜間にフルバックアップを取っていても、それだけでは夜から障害までの間の変更は戻せません。フルバックアップで基準の状態を戻し、そのあとのバイナリログを指定した時点まで再生すると、「午後 2 時 29 分の状態」のように時点を選んで復旧できます。これがポイントインタイムリカバリ (PITR) です。

「誤って DELETE や DROP TABLE を実行した」というときに使います。直前の状態までログを再生し、問題の操作の手前で止めます。

設定の確認

MySQL 8.0 ではバイナリログは標準で有効です。5.7 以前では log_bin を設定します。保存期間は binlog_expire_logs_seconds で決まり、標準は 30 日です。

SHOW VARIABLES LIKE 'log_bin';
SHOW VARIABLES LIKE 'binlog_expire_logs_seconds';
SHOW BINARY LOGS;

ログは古いものから自動で消えるため、フルバックアップの保存期間よりログの保存期間が短いと途中の変更がつながりません。ログも別の場所にコピーして保管してください。

リストアの手順

たとえば、2026-11-08 14:30 に demo_shop.orders を誤って消した場合です。

# 1. フルバックアップを戻す (前節までの手順)
# 2. バックアップを取った位置から、誤操作の直前までログを再生する
mysqlbinlog --start-position=157 --stop-datetime="2026-11-08 14:29:59" \
  --database=demo_shop binlog.000042 binlog.000043 | mysql -u root -p

時刻の指定は粗くなりやすいので、まず mysqlbinlog の出力を読んで問題の操作の位置 (at 12345) を見つけ、--stop-position=12345 で止めるほうが確かです。

再生時の落とし穴

  • 形式が ROW のログは mysqlbinlog -v で中身を確認できる。再生する前に読んでおく
  • 再生の開始位置はフルバックアップを取った時点のログの位置。mysqldump なら --source-data=2 で残したコメントで、XtraBackup なら xtrabackup_binlog_info ファイルで分かる
  • 誤操作そのものを再生しないように、停止位置を必ず確認する

5. RDS・Aurora のスナップショット

Amazon RDS for MySQL と Aurora MySQL では、バックアップの仕組みがサービスに組み込まれています。

  • 自動バックアップ: 毎日 1 回のスナップショットとトランザクションログを自動で保存。保持期間は 0〜35 日で、期間内の任意の時刻 (直近の数分前まで) に戻せる
  • 手動スナップショット: 好きなタイミングで取り、自分で消すまで残る。バージョンアップや大きな変更の前に取っておくもの

Aurora はストレージが自動で継続的にバックアップされ、保持期間は 1〜35 日です。RDS と同じく時点を指定して戻せます。

マネージド環境を使っているなら、基本はこれです。物理バックアップなので大きな DB でも取る時間は短く、テーブルのロックもかかりません。

手動スナップショットを取る

aws rds create-db-snapshot \
  --db-instance-identifier demo-shop-db \
  --db-snapshot-identifier demo-shop-before-migration-20261108

# 自動バックアップの保持期間を 14 日にする
aws rds modify-db-instance \
  --db-instance-identifier demo-shop-db \
  --backup-retention-period 14 \
  --apply-immediately

リストア

RDS のリストアは既存のインスタンスを上書きせず、新しいインスタンスを作る形です。

# スナップショットから新しいインスタンスを作る
aws rds restore-db-instance-from-db-snapshot \
  --db-instance-identifier demo-shop-db-restored \
  --db-snapshot-identifier demo-shop-before-migration-20261108

# 時点を指定して新しいインスタンスを作る (PITR)
aws rds restore-db-instance-to-point-in-time \
  --source-db-instance-identifier demo-shop-db \
  --target-db-instance-identifier demo-shop-db-pitr \
  --restore-time 2026-11-08T05:29:00Z

時刻は UTC で指定します (日本時間の 14:29 は UTC の 05:29)。

RDS 固有の落とし穴

  • 戻したインスタンスはエンドポイントが変わる。パラメータグループやセキュリティグループなど引き継がれない設定があるため、戻したあとに確認が要る
  • 自動バックアップはインスタンスを削除すると消える。削除前に最終スナップショットを取り、手動スナップショットは別リージョンや別アカウントへのコピーも検討する
  • スナップショットは DB を丸ごと戻すもの。1 つのテーブルだけ戻したいときは、戻したインスタンスから該当テーブルをダンプして本番に流し込む
  • Aurora MySQL ではバックトラックを有効にしておくと、新しいクラスターを作らずに時間を巻き戻せる

バックアップが動いているかの確認方法

バックアップは取れていることを確かめ続けて初めて役に立ちます。

1. 実行ログを見る

cron で動かしているなら、終了コードとログを残して失敗したら通知が届くようにします。

#!/bin/bash
set -euo pipefail
OUT=/backup/demo_shop_$(date +%Y%m%d_%H%M).sql.gz
mysqldump --defaults-extra-file=/etc/mysql/backup.cnf --single-transaction demo_shop | gzip > "$OUT"
echo "$(date -Is) ok $(stat -c %s "$OUT") bytes" >> /var/log/mysql_backup.log

set -o pipefail がないと、パイプの前段 (mysqldump) が失敗しても後段の gzip が成功したことになり、中身が空のファイルが正常終了として残ります。

RDS では aws rds describe-db-snapshots で最新のスナップショットの時刻と状態を見るか、EventBridge でバックアップ失敗のイベントを通知に流します。

2. ファイルサイズの推移を見る

毎日のファイルサイズを並べると異常に気づけます。普段 2 GB のファイルが突然 200 MB になっていたら、テーブルが抜けたか途中で切れています。急に倍になっていればデータの異常な増加です。

サイズの推移は監視ツールにメトリクスとして送っておくとグラフで確認できます。

3. 実際に戻してみる

いちばん確かなのは、定期的にリストアを試すことです。別のサーバー (または一時的な RDS インスタンス) に戻して確かめます。

-- 行数が本番と近いか
SELECT COUNT(*) FROM demo_shop.orders;

-- 最新のデータの時刻が、バックアップを取った時刻と合っているか
SELECT MAX(created_at) FROM demo_shop.orders;

-- テーブルの数が揃っているか
SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'demo_shop';

四半期に 1 回でも構いません。手順書の不備や権限の不足、復旧にかかる時間は実際にやってみないと分かりません。

よくある失敗

ロックで本番が止まる

--single-transaction を付け忘れた mysqldump はテーブルに読み取りロックをかけるため、書き込みが待たされます。注文のような書き込みの多い DB ではこれだけで画面が固まります。

InnoDB なら必ず --single-transaction を付け、ピーク時間を避けて実行します。MyISAM が混ざる場合やロックが避けられない場合はレプリカから取るのが安全です。ロック待ちが連鎖する仕組みは「ロック待ちの原因と対処」で説明しています。

文字コードの問題

ダンプとリストアで文字コードが合わないと、日本語が ? や文字化けになります。ダンプ時に --default-character-set=utf8mb4 を指定し、戻すときも同じ指定でクライアントを動かします。

Windows の PowerShell でリダイレクト (>) を使うと UTF-16 で保存されて戻せなくなることがあるため、--result-file オプションを使うのが安全です。

リストアを試したことがない

バックアップのファイルがあることと、戻せることは別の話です。ファイルが壊れていた、権限が足りなくて戻せなかった、戻すのに丸一日かかった、という話は珍しくありません。復旧にかかる時間を障害の前に一度計っておきましょう。

バックアップの設定は、一度作ったら忘れがちです

ここまでの設定は一度作れば動き続けます。ただ、動き続けていることと役に立つ状態であることは別です。ディスクの空きが減ってダンプが途中で切れる、テーブルが増えたのにバックアップの対象に入っていない、RDS の保持期間が誰かの変更で短くなっている。こうした変化はバックアップが必要になるまで気づけません。

Scandock は DB の状態を AI が継続的に監視するサービスです。接続文字列を登録すると、AI がテーブルの構成と実データを読み、注文数やジョブの滞留のような事業の数字も含めて何を監視するかを決めます。固定の閾値ではなく値の推移を踏まえて異常かどうかを判断し、異常と判断すると読み取り専用のクエリで関連データを調べ、原因・影響・根拠・対処案をつけた通知を送る仕組みです。バックアップの記録をテーブルに残しているなら、「最新のバックアップの日時が止まっていないか」といった確認も監視の対象にできます。

バックアップそのものはこの記事で紹介した仕組みで取ります。Scandock が見るのは DB の中で起きている変化です。DB 監視の考え方は「データベース監視とは」にまとめています。

参考

関連する記事