今、MySQLのバックアップを作り直すとしたら何がどう良いのかを考える旅 〜YAPC::Fukuoka 2025レポート〜

はじめに
2025年11月14-15日(金土)に、福岡工業大学にてYAPC::Fukuoka 2025が開催されました。本記事ではYAPCにおけるトークの中から、@yoku0825こと田中翼(yoku)さんのトーク「今、MySQLのバックアップを作り直すとしたら何がどう良いのかを考える旅」についてレポートします。
yokuさんはデータベース、特にMySQLを専門とするエンジニアで、日本MySQLユーザ会副代表も務めています。2025年5月にさくらインターネットに入社しました。入社直後に「さくマガ」にて実施したインタビュー記事もありますのでぜひご覧ください。
ガバメントクラウドにおけるデータベースのバックアップ要件
さくらインターネットは現在、ガバメントクラウドの正式認定を受けるために、さくらのクラウドに多数の機能を追加すべく鋭意開発を進めています。ガバメントクラウドの公募ページには調達仕様書が掲載されており、全部で300項目ほどの技術要件が記載されています。その中にはデータベースに関する項目もあるのですが、今回のトークで焦点となるのはデータベースのバックアップに関する要件で、以下の4点が記載されています。
- DBのバックアップを差分で取得できること。取得したバックアップはいつでもDBの復元に使用できること。復元時は別のサイズを選択できること。
- DBのバックアップは、一定期間(例:5分)以前のどの時点にでも戻せるかたちで(Point In Timer Restore)取得できること。
- DBのバックアップは、予め設定したタイミング、周期、世代で自動的に定期実行できること。
- DBのバックアップイメージは、CSPの中であれば、別のシステム、別のネットワーク、別の環境からも利用できること。
これらの要件を満たすような機能をさくらのクラウドに実装する必要があり、yokuさんはこれに取り組みました。特に以下の2点が考えどころになるとのことでした。
- DBのバックアップを差分で取得できること
- どの時点にでも戻せるかたちで(Point In Timer Restore(PITR))取得できること
バックアップ手法あれこれ
どのように実装したかを説明する前に、データベースのバックアップ手法を解説しました。手法としては以下の3つがあります。
- フルバックアップ: データベース全体のバックアップを取得する
- 差分バックアップ: 「前回のフルバックアップ」と「現在のデータベース」の差分を取得する
- 増分バックアップ: 「前回のバックアップ」と「現在のデータベース」の差分を取得する
増分バックアップは、「前回のバックアップ」がフルバックアップとは限らない点が差分バックアップと異なります。
例として、1月1日から1月8日にかけて、3種類のバックアップを混在して実施したときの様子を図にしたものを以下に掲げます。
PITRの実装
今回実装するPITR機能は、上述した3種類の手法を組み合わせながら要件を満たすようなバックアップを取得するものです。
MySQLにおけるPITRはバイナリログを使って実現しています。バイナリログとは、データベースの変更操作を記録したものです。PITRは、フルバックアップを取った時点のバイナリログ情報を記録しておき、それ以降のバイナリログを継続的にコピーし続けることによって実現できます。バイナリログは上述のバックアップ手法に照らし合わせると増分バックアップに相当するもので、ガバメントクラウドの要件に書いてある「バックアップを差分で取得」とは厳密には異なりますが、PITRを実現するにはフルバックアップとバイナリログだけで実装する方がシンプルです。
また、ガバメントクラウドの要件には「一定時間(例:5分)以前のどの時点にも戻せる形で取得できること」という記述がありますが、これは「少なくとも5分に1回バイナリログをバックアップすればよい」と解釈できます。今回はmysqlbinlogコマンドでバイナリログをストリーミング出力させ、バイナリログが出力され次第すぐにバックアップとして保管されるような仕組みにしてこれを満たすようにしました。この場合のバイナリログのバックアップとリストアのコマンドは以下のようになります。
### バックアップ
$ target_binlog="$(basename $(ls /path/to/backup/binlog_mysqlbinlog/*.[0-9]* | tail -1 2> /dev/null))"
$ [[ -z $target_binlog ]] && target_binlog="$(mysql -uroot -sse 'SHOW BINARY LOGS' | head -1 | awk '{print $1}')"
$ mysqlbinlog -uroot -R --stop-never --raw $target_binlog
### リストア
### 「このバイナリログとそれ以降」をmysqlbinlogコマンドに引数にしたい
$ target_binlog="$(cat $target_diff/xtrabackup_binlog_info | awk '{print $1}')"
$ binlogs=$(for n in $(seq $binlog_sequence 1000000) ; do
binlog_file="$(printf '/path/to/backup/binlog_mysqlbinlog/binlog.%06d' $n)"
[[ -e $binlog_file ]] || break
echo $binlog_file
done)
### --start-positionはGTIDが有効ならそんなに厳密でなくていいので無視
$ mysqlbinlog --stop-datetime="$target" $binlogs | mysql -uroot
リストアの流れとしては以下のようになります。
- 戻したい時刻を指定
- それよりも一番近いフルバックアップを入手
- 2のフルバックアップよりも後、かつ戻したい時刻よりも前で最新の差分バックアップを入手し適用
- 戻したい時刻と差分バックアップの間のバイナリログを入手し適用
MySQLのフルバックアップ
MySQLのフルバックアップを取得するツールはいくつかあります。以下に例示します。
- コールドバックアップ
- MySQL Enterprise Backup
- Percona XtraBackup
- CLONE INSTANCE
- mysqldump
- MySQL Shell Dump/Load Utility
- MyDumper
この中で有名なのはmysqldumpですが、yokuさんいわく遅いのでおすすめしないとのことです。差分が取れるフルバックアップツールとしては、MySQL Enterprise Backup(MEB)とPercona XtraBackup(xb)の2つがあります。これらのツールでは、ibdファイル(InnoDBのデータファイル)をコピーしている間、ずっとInnoDBログをコピーし続けることによってバックアップを取得します。リストアは、バックアップ時にコピーしたibdファイルとInnoDBログを使って実行します。このようにすればバックアップ要件を満たすことができそうです。
要件を満たすバックアップとリストアの実装
xtrabackupを使ったフルバックアップと差分バックアップの実行例を以下に示します。ここには記載していませんが、これとは別に増分バックアップは常に取り続けます。(リストアするときに必要になるため)
### フルバックアップ
$ xtrabackup -uroot --backup --target-dir="/path/to/backup/xb_full_$(date '+%Y%m%d_%H%M%S')"
### 差分バックアップ
$ latest_backup="$(ls -d /path/to/backup/xb_full_* | tail -1)"
$ xtrabackup -uroot --backup --incremental-basedir="$latest_backup" --target-dir="/path/to/xb_diff_$(date '+%Y%m%d_%H%M%S')"
一方、リストア時の実行例は以下のようになります。
### フルバックアップからのリストア
$ target="2025-11-12 14:30:00"
$ target_full=$(dirname $(find /path/to/backup/xb_full* -type f -name "xtrabackup_binlog_info" ! -newermt "$target" | sort | tail -1))
$ cp -r ${target_full}/* ./
$ xtrabackup --prepare --apply-log-only --target-dir=./ ### 差分バックアップがない時は --apply-log-only をつけてはいけない
### 差分のリストア
$ target_diff=$(dirname $(find /path/to/backup/xb_diff* -type f -name "xtrabackup_binlog_info" ! -newermt "$target" | sort | tail -1))
$ xtrabackup --prepare --target-dir=./ --incremental-dir="$target_diff"
また、mysqlshという、MySQLに標準添付されるコマンドラインクライアントよりも高機能なコマンドラインクライアントを使うことでも、以下のようにバックアップとリストアを実行することができます。
### フルバックアップ
$ mysqlsh --login-path=backup -h ipaddr --js -- util dumpInstance /path/to/mysqlsh_full_$(date '+%Y%m%d_%H%M%S')
### リストア対象の識別
$ target="2025-11-12 14:30:00"
$ target_full=$(dirname $(find /path/to/backup/mysqlsh_full* -type f -name "@.json" ! -newermt "$target" | sort | tail -1))
### @.jsonからバイナリログの情報も取り出せる -> mysqlbinlogの最初のファイルを指定するのに使える
$ jq '.binlogFile, .binlogPosition, .gtidExecuted' @.json
"bin.000005"
948
"aefbe16b-aedd-11f0-8a5f-02001702f486:1-2188"
まとめ
ガバメントクラウドにおけるデータベースのバックアップ要件と、その要件を満たすようなバックアップおよびリストアの実装について検討した結果をお伝えしました。まとめると以下のようになります。
- MySQLのバックアップはxtrabackupとバイナリログを使うことで実現できる
- mysqlshを使う方法もある。フルバックアップも増分バックアップもmysqlshコマンドで取得可能
本記事では紹介しきれなかった点もたくさんあります。それらについてはyokuさんが公開している発表資料をご覧ください。
また、yokuさんのブログやGitHubにも関連情報がありますので、さらに詳しく知りたい方はこちらもご覧ください。
- 日々の覚書: 最新のMySQL ShellだけでMySQLのPITR可能なバックアップを設定する(フルバックアップ編)
- 日々の覚書: 最新のMySQL ShellだけでMySQLのPITR可能なバックアップを設定する(増分バックアップ編)
- 日々の覚書: 最新のMySQL ShellだけでMySQLのPITR可能なバックアップを設定する(実践編)
- yoku0825/simple-mysqlsh-pitr: PoC of PITR only using MySQL Shell
おわりに
今回の成果は、さくらのクラウドのデータベースアプライアンスにおけるPITR機能としてリリースされています。
なお、PITR機能が使えるのはMariaDBのみです。MariaDBはMySQLから派生したソフトウェアですが、現在は互換性がなくなっているため、今回検討したものがそのまま実装されているわけではありません。それでも、こうして検討した成果によってガバメントクラウドの要件をいくつかクリアできたことは間違いありません。このような成果を積み重ね、ガバメントクラウドの正式認定を受けられることを目指して、これからもさくらのクラウドは前進を続けていきます。
それではまた次回のイベントでお会いしましょう!