Percona Xtrabackup 利用されておりますでしょうか。
今回は、運用を便利にする Percona Xtrabackup の履歴機能の活用方法についてご紹介させていただきます。
Percona Xtrabackupの履歴機能
Percona Xtrabackupでは、 --backup
時に --history
オプションをつけることで PERCONA_SCHEMA.xtrabackup_history
テーブルに実行されたバックアップの履歴を取ることができます。
Store backup history on the server
例えば以下のようにバックアップを取得すると
1 |
xtrabackup --backup --history --target-dir=/tmp/$(date +%F)_base_backup |
履歴テーブルが作成され、実行履歴が格納されています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
mysql> select * from PERCONA_SCEHMA.xtrabackup_history\G *************************** 1. row *************************** uuid: 19c7b07a-91b7-11ef-be67-525400f51f05 name: NULL tool_name: xtrabackup tool_command: --backup --history=2024-10-24 --target-dir=/tmp/2024-10-24_base_backup tool_version: 8.4.0-1 ibbackup_version: 8.4.0-1 server_version: 8.4.2 start_time: 2024-10-24 03:21:44 end_time: 2024-10-24 03:21:46 lock_time: 1 binlog_pos: filename 'binlog.000006', position '198', GTID of the last change '2bd3f163-8603-11ef-9f97-525400f51f05:1-3308' innodb_from_lsn: 0 innodb_to_lsn: 46521144 partial: N incremental: N format: file compact: N compressed: N encrypted: N 1 row in set (0.00 sec) |
これだけでも、バックアップにどのくらい時間がかかっていたのか、圧縮や暗号化をかけたときとそうでない時の速度差はどうかなど、有用な情報を確認できる気がします。
xtrabackup自体にも、この履歴テーブルの情報を使って、増分バックアップの取得を便利にするオプションが存在しています。
履歴テーブルをつかって増分バックアップを取る
まず、増分バックアップの最もシンプルな取得方法は、 --incremental-basedir=[base backup dir]
オプションによりベースバックアップを指定することです。
1 |
xtrabackup --backup --incremental-basedir=/tmp/20241001_base_backup --target-dir=/tmp/$(date +%F)_inc1_backup |
この場合、ベースディレクトリ名を知る必要がありますが、一般的にバックアップを取得する場合はジョブスケジューラにワンライナーやスクリプトを登録して行うため、これについてのロジックを考える必要が発生します。
差分バックアップとするなら、例としては 「同じディレクトリ内の full というプレフィクスがつくバックアップの最も新しいディレクトリをベースバックアップとする」というような感じになるでしょうか。
1 |
base_backup=$(ls -td /path/to/backup/full* | head -1) |
増分バックアップとするなら、大まかには「同じディレクトリ内の inc というプレフィクスがつくバックアップの最も新しいディレクトリをベースバックアップとする」のようになるでしょう。
1 |
base_backup=$(ls -td /path/to/backup/inc* | head -1) |
ですが、この場合ディレクトリの命名ルールを設けたり、意図せずバックアップディレクトリのタイムスタンプがズレたりしたときに正常に動作しない可能性があり、それらについてロジックで対応していくのもひと手間です。
そこで、履歴テーブルを活用してよりスクリプティングを効率化することができます。
先程の履歴では name
列が NULL
になっていました。
この name
列は --history=[name]
のように指定する事ができます。 この名前のことを バックアップ・シリーズ名(シリーズ名)
と呼称します。
1 |
xtrabackup --backup --history=backup-series1 --target-dir=/tmp/$(date +%F)_base_backup |
シリーズ名が付きました。
tool_command
の --target-dir
から、どこにバックアップを取得したのかもわかりますね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
mysql> select * from PERCONA_SCHEMA.xtrabackup_history\G *************************** 1. row *************************** uuid: c20f1853-9267-11ef-be67-525400f51f05 name: backup-series1 tool_name: xtrabackup tool_command: --backup --history=backup-series1 --target-dir=/tmp/2024-10-25_base_backup tool_version: 8.4.0-1 ibbackup_version: 8.4.0-1 server_version: 8.4.2 start_time: 2024-10-25 00:26:17 end_time: 2024-10-25 00:26:20 lock_time: 1 binlog_pos: filename 'binlog.000007', position '198', GTID of the last change '2bd3f163-8603-11ef-9f97-525400f51f05:1-31820' innodb_from_lsn: 0 innodb_to_lsn: 137186580 partial: N incremental: N format: file compact: N compressed: N encrypted: N |
履歴テーブルの各列は以下の意味です。
PERCONA_SCHEMA.xtrabackup_history
Column Name | Description |
---|---|
uuid | ユニークなバックアップのID |
name | –historyオプションでユーザーが指定したバックアップシリーズの名前 |
tool_name | バックアップを取得するために使用されるツールの名前 |
tool_command | -–passwordと-–encrypt_keyを隠避したツールに指定された正確なコマンドライン |
tool_version | バックアップの作成に使用されたツールのバージョン |
ibbackup_version | バックアップの作成に使用されたxtrabackupバイナリのバージョン |
server_version | バックアップが作成されたサーバのバージョン |
start_time | バックアップの開始時刻 |
end_time | バックアップ終了時の時刻 |
lock_time | FLUSH TABLES WITH READ LOCKのロックの呼び出しと保持に費やされた時間 (秒単位) |
binlog_pos | FLUSH TABLES WITH READ LOCK終了時のバイナリログファイルと位置 |
innodb_from_lsn | 以前のバックアップを判別するために使用できるバックアップ開始時のLSN |
innodb_to_lsn | 次の増分の開始lsnとして使用できるバックアップ終了時のLSN |
partial | 部分バックアップかどうか。Nの場合は完全バックアップであることを意味します。 |
incremental | 増分バックアップかどうか。 |
format | 出力形式の説明 (xbstream) |
compressed | 圧縮されたバックアップかどうか |
encrypted | 暗号化されたバックアップかどうか |
履歴テーブルの情報を元に増分バックアップを取るには、 --incremental-history-name
オプションを使用します。
--incremental-history-name
にはシリーズ名を指定し、そのバックアップシリーズの内、「 最新の (最大のto_lsn) バックアップを探し、最初のlsnとして使用するto_lsn値を取得します。」 とドキュメントにあります。
実際の検索SQLは以下のとおりです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
static bool select_incremental_lsn_from_history(lsn_t *incremental_lsn) { : 略 : if (opt_incremental_history_name) { mysql_real_escape_string(mysql_connection, buf, opt_incremental_history_name, strlen(opt_incremental_history_name)); snprintf(query, sizeof(query), "SELECT innodb_to_lsn " "FROM PERCONA_SCHEMA.xtrabackup_history " "WHERE name = '%s' " "AND innodb_to_lsn IS NOT NULL " "ORDER BY innodb_to_lsn DESC LIMIT 1", buf); } |
それでは --incremental-history-name
を使用して2回ほど増分バックアップを取ってみましょう。
1 2 3 4 5 6 7 |
# 1回目 xtrabackup --backup --history=backup-series1 --target-dir=/tmp/$(date +%F)_inc1_backup \ --incremental-history-name=backup-series1 # 2回目 xtrabackup --backup --history=backup-series1 --target-dir=/tmp/$(date +%F)_inc2_backup \ --incremental-history-name=backup-series1 |
以下の通り、最新のLSNをベースに増分バックアップを取る事ができました。
見ての通りですが、つねにベースバックアップは最新のLSNを持つものを選択するので、これは増分バックアップということになり、リストア時は複数の増分バックアップを適用する事になります。
1 2 3 4 5 6 7 8 |
mysql> select name,incremental,REGEXP_SUBSTR(tool_command, '--target-dir=.*?( |$)') target_dir,innodb_from_lsn, innodb_to_lsn from xtrabackup_history; +----------------+-------------+-------------------------------------------+-----------------+---------------+ | name | incremental | target_dir | innodb_from_lsn | innodb_to_lsn | +----------------+-------------+-------------------------------------------+-----------------+---------------+ | backup-series1 | N | --target-dir=/tmp/2024-10-25_base_backup | 0 | 137186580 | | backup-series1 | Y | --target-dir=/tmp/2024-10-25_inc1_backup | 137186580 | 222356795 | | backup-series1 | Y | --target-dir=/tmp/2024-10-25_inc2_backup | 222356795 | 241744069 | +----------------+-------------+-------------------------------------------+-----------------+---------------+ |
履歴テーブルをつかって差分バックアップを取る
差分バックアップとしたい場合は、 --incremental-history-uuid
が役立ちます。
あるシリーズ内の最も新しいフルバックアップの行を取得するSQLを書きます。
1 2 3 4 5 6 7 8 9 10 11 |
mysql> SELECT uuid -> FROM PERCONA_SCHEMA.xtrabackup_history -> WHERE name = "backup-series1" -> AND innodb_from_lsn = 0 -> AND incremental = 'N' AND partial = 'N' -> ORDER BY start_time DESC LIMIT 1; +--------------------------------------+ | uuid | +--------------------------------------+ | c20f1853-9267-11ef-be67-525400f51f05 | +--------------------------------------+ |
上記のSQLの結果を --incremental-history-uuid
オプションに使います。
以下のような感じになるでしょう。
1 2 3 4 5 6 7 8 9 10 11 12 |
$ backup_uuid=$(mysql -NB <<-EOSQL SELECT uuid FROM PERCONA_SCHEMA.xtrabackup_history WHERE name = "backup-series1" AND innodb_from_lsn = 0 AND incremental = 'N' AND partial = 'N' ORDER BY start_time DESC LIMIT 1; EOSQL ) $ xtrabackup --backup --history=backup-series1 --target-dir=/tmp/$(date +%F)_inc3_backup \ --incremental-history-uuid=${backup_uuid} |
incemental=N(フルバックアップ)のinnodb_to_lsnをベースに差分バックアップを取得する事ができました。
1 2 3 4 5 6 7 8 |
+----------------+-------------+-------------------------------------------+-----------------+---------------+ | name | incremental | target_dir | innodb_from_lsn | innodb_to_lsn | +----------------+-------------+-------------------------------------------+-----------------+---------------+ | backup-series1 | Y | --target-dir=/tmp/2024-10-25_inc3_backup | 137186580 | 373899394 | | backup-series1 | N | --target-dir=/tmp/2024-10-25_base_backup | 0 | 137186580 | | backup-series1 | Y | --target-dir=/tmp/2024-10-25_inc1_backup | 137186580 | 222356795 | | backup-series1 | Y | --target-dir=/tmp/2024-10-25_inc2_backup | 222356795 | 241744069 | +----------------+-------------+-------------------------------------------+-----------------+---------------+ |
バックアップスクリプトを作る
バックアップスクリプトにする場合も、よりわかりやすく記述できるのではと思います。
pxb-backup.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#!/bin/bash set -eu -o pipefail usage() { echo "Usage: $0 [full|incr] [BACKUP_SERIES_NAME] [BACKUP_NAME]"; exit 1; } MYSQL_CLIENT_CONFIG=/path/to/my.client.cnf # 認証のための設定ファイル DEST=/backup [ ${#@} -ge 2 ] || usage [[ "${1}" =~ full|incr ]] || usage BACKUP_TYPE=${1} SERIES_NAME=${2} BACKUP_NAME=${3} if [[ "${BACKUP_TYPE}" =~ full ]];then xtrabackup --defaults-file=${MYSQL_CLIENT_CONFIG=} \ --backup --history=${SERIES_NAME} \ --target-dir=${DEST}/${BACKUP_NAME} elif [[ "${BACKUP_TYPE}" =~ incr ]];then # base backupの位置などを気にせずコマンドだけで増分バックアップが可能 xtrabackup --defaults-file=${MYSQL_CLIENT_CONFIG=} \ --backup --history=${SERIES_NAME} \ --target-dir=${DEST}/${BACKUP_NAME} \ --incremental-history-name=${SERIES_NAME} fi |
cronに設定するならこのようになるでしょう。
1 2 3 4 |
# 日曜日にベースバックアップ、月~土曜日に増分バックアップ # シリーズ名はベースバックアップのもの(YYYY-MM-DD)を基準とする 0 0 * * sun /path/to/backup.sh full $(date +\%F) full-$(date +\%F) 2> /var/log/pxb-backup.log 0 0 * * mon-sat /path/to/backup.sh incr $(date -d "last sunday" +\%F) incr-$(date +\%F) 2> /var/log/pxb-backup.log |
バックアップをリモートに送るためや、ファイルとして扱いたいために、 --stream=xbstream
として出力しているユーザも多いかと思います。
圧縮なども一般的に行われるでしょう。
1 2 3 |
xtrabackup --backup --history=remote-backup --target-dir=/tmp --stream=xbstream \ 2> error.log \ | pzstd -p 10 --stdout > backup.xb.zst |
--incremental-basedir=[basebackupdir]
とした場合は、バックアップ内のメタデータファイル(xtrabackup_info)から開始地点のLSNを取得していますが、xbstream形式でアーカイブされていたりするとxtrabackup_infoが読めない状況になります。
その際のLSNを確認し、指定する方法としては、まず、バックアップ時のログから以下の行を検索し、 --incremental-lsn=[start lsn]
のように指定する必要がありました。
1 |
2024-10-25T06:27:33.993991-00:00 0 [Note] [MY-011825] [Xtrabackup] Transaction log of lsn (631060112) to (634877497) was copied. |
これをスクリプトにしようと思うと面倒ですが、履歴テーブルがあれば出力形式にかかわらず開始地点をテーブルから確認できますので、 xbstream
ユーザにとっても嬉しい機能かと思います。
バックアップローテーションでも活用できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# ある日時よりも前の復旧地点になるシリーズを削除する mysql -NB <<-EOSQL | while read expired_backup SELECT SUBSTRING_INDEX(REGEXP_SUBSTR(tool_command, '--target-dir=.*?( |$)'),'=',-1) target_dir FROM PERCONA_SCHEMA.xtrabackup_history WHERE name in ( SELECT name FROM PERCONA_SCHEMA.xtrabackup_history -- シリーズごとの最新復旧地点を指定した日時と比較 GROUP BY name HAVING max(start_time) < "${1}"; ) EOSQL do echo "${expired_backup}" >&2 rm -rf "${expired_backup}" done |
やたらと長くなってしまいましたが、あるシリーズのリストア用コマンドを出力するなどもできます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
#!/bin/bash -eu mysql -NB <<-EOSQL SET @series = "${1}"; SET @dest = "${2}"; SELECT TRIM(SUBSTRING_INDEX(REGEXP_SUBSTR(tool_command, '--target-dir=.*?( |$)'),'=',-1)), start_time INTO @base_backup_dir, @base_start_time FROM PERCONA_SCHEMA.xtrabackup_history WHERE name = @series AND innodb_from_lsn = 0 AND incremental = 'N' AND partial = 'N' ORDER BY start_time DESC LIMIT 1; SELECT uuid INTO @latest_backup_uuid FROM PERCONA_SCHEMA.xtrabackup_history WHERE name = @series ORDER BY start_time DESC LIMIT 1; SELECT CASE WHEN uuid = @latest_backup_uuid THEN CASE WHEN incremental = 'Y' THEN CONCAT( 'xtrabackup' , ' --prepare' , ' --target-dir="', @base_backup_dir, '"' , ' --incremental-dir="' , TRIM(SUBSTRING_INDEX(REGEXP_SUBSTR(tool_command, '--target-dir=.*?( |$)'),'=',-1)) , '"' ) ELSE CONCAT( 'xtrabackup' , ' --prepare' , ' --target-dir="' , TRIM(SUBSTRING_INDEX(REGEXP_SUBSTR(tool_command, '--target-dir=.*?( |$)'),'=',-1)) , '"' ) END ELSE CASE WHEN incremental = 'Y' THEN CONCAT( 'xtrabackup' , ' --prepare' , ' --apply-log-only' , ' --target-dir="', @base_backup_dir, '"' , ' --incremental-dir="' , TRIM(SUBSTRING_INDEX(REGEXP_SUBSTR(tool_command, '--target-dir=.*?( |$)'),'=',-1)) , '"' ) ELSE "" END END FROM PERCONA_SCHEMA.xtrabackup_history WHERE name = @series AND start_time >= @base_start_time; VALUES ROW(CONCAT('xtrabackup --copy-back --target-dir=', @base_backup_dir, ' --datadir=', @dest)) , ROW(CONCAT('chown -R mysql. ', @dest)) , ROW('systemctl start mysqld.service') ; EOSQL |
以下のようにリストアに必要なコマンドが出力されました。
1 2 3 4 5 6 7 8 |
# ./restore.sh series1 /var/lib/mysql xtrabackup --prepare --apply-log-only --target-dir="/backup/base" --incremental-dir="/backup/incr1" xtrabackup --prepare --apply-log-only --target-dir="/backup/base" --incremental-dir="/backup/incr2" xtrabackup --prepare --target-dir="/backup/base" --incremental-dir="/backup/incr3" xtrabackup --copy-back --target-dir=/backup/base --datadir=/var/lib/mysql chown -R mysql. /var/lib/mysql systemctl start mysqld.service |
最新の状態まで復旧するには、存在しているバイナリログを適用する必要がありますが、履歴テーブルにはバックアップ時点のバイナリログ状況も記録されています。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
mysql> set @series = 'series1'; Query OK, 0 rows affected (0.00 sec) mysql> SELECT -> binlog_pos -> FROM PERCONA_SCHEMA.xtrabackup_history -> WHERE name = @series -> ORDER BY start_time DESC LIMIT 1; +-------------------------------------------------------------------------------------------------------------------+ | binlog_pos | +-------------------------------------------------------------------------------------------------------------------+ | filename 'binlog.000022', position '198', GTID of the last change 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-100000' | +-------------------------------------------------------------------------------------------------------------------+ |
上記の場合は、binlog.000022 以降のファイルを適用すれば良く、GTID aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-100000 までは適用済みと判断できるため、以下のコマンドを実行すれば最新までロールフォワードできます。
1 2 3 4 5 6 7 8 |
# 必要なバイナリログをfindし、mysqlbinlogに渡してリカバリ用SQLを作成 $ mysqlbinlog -v $(find /var/lib/mysql -regex '/path/to/binlog.[0-9]+' \( -name 'binlog.000022' -o -newer /path/to/binlog.000022 \)) > recovery.sql # 不要なGTIDを設定 $ mysql -e "RESET MASTER;SET GTID_PURGED='aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-100000'" # リカバリの実行 $ mysql < recovery.sql |
サマリ
SQLで必要な情報をクエリできるというのは、今更ながらとても便利に感じました。
xtrabackupを使用しているユーザは多いと思いますが、履歴テーブルを使いこなしているユーザは少数派ではないでしょうか。
興味を持たれたユーザ様はぜひ利用をご検討してみてください!