はじめに
MySQL Enterprise Backup(MEB) 8.0.20 以降のバージョンでは、フルバックアップから特定のテーブルをリストアできるようになりました。
For MySQL Enterprise Backup 8.0.20 and later:
Table-Level Recovery (TLR) now allows selective restores of tables or schemas from full backups; see Section 5.1.4, “Table-Level Recovery (TLR)” for details.
引用 :MySQL :: MySQL Enterprise Backup 8.0 User’s Guide :: 3 What’s New in MySQL Enterprise Backup 8.0?
マニュアル上では、Table-Level Recovery (TLR)
もしくは、partial restore
とも表記されています。
これまでのバージョン(~8.0.19)では、予め取得済みの部分バックアップからの部分リストアは可能です。
更に言えば、InnoDBストレージエンジンのテーブルに対しての部分バックアップは、リストア時の煩雑さやエラー発生を極力回避するために、トランスポータブルテーブルスペース機能(TTS)を使用してバックアップ(--include-table='some_table' --use-tts
)することがほぼ必須でした。
MySQL :: MySQL Enterprise Backup 8.0 User’s Guide :: 4.3.5 Making a Partial Backup
しかしながら、このTTSバックアップは制約事項が多いため、結果的に採用を見送ることになる、ということも想像に難くありません。
※具体的な制約事項はマニュアルの以下のページでご確認ください。
MySQL :: MySQL Enterprise Backup 8.0 User’s Guide :: 19.8 Partial Backup and Restore Options
MySQL :: MySQL Enterprise Backup 8.0 User’s Guide :: B Limitations of MySQL Enterprise Backup
また、フルバックアップで取得しているのに、特定のテーブルのみ重複して個別でバックアップを取っておかなければならないという点も効率があまりよくありません。
一方、フルバックアップからどうにかしてある特定のテーブルのみをリストアしたい場合どのような手法があるかというと…
- リカバリ対象のMySQLとは別のインスタンス、およびフルバックアップをリストア可能なディスクスペースを別途用意
- そこへフルリストア後、mysqldumpなどで対象テーブルのデータを抽出
という間接的でコストが掛かる方法ぐらいしかありませんでした。
本記事では、部分リストアがよりシンプルに実現できるようになったTable-Level Recovery (TLR)
を確認してみたいと思います。
要件と制約を確認してみる
マニュアルに記載されている、部分リストアを使用するための要件と、使用上の制約や注意事項を(ほぼ素文直訳ですが)要約してみました。
MySQL :: MySQL Enterprise Backup 8.0 User’s Guide :: 5.1.4 Table-Level Recovery (TLR)
要件
- リストア先サーバーが実行中であること
- リストア先サーバーとバックアップ作成サーバーのページサイズが同じであること
※これは通常は同一サーバー・インスタンスでバックアップするのですが、バックアップを別サーバーでリストアしたい場合の要件になると思います。 - リストア先サーバーで
innodb_file_per_table
オプションを有効にする - 非TTSバックアップの場合、リストアするテーブルは、リストア先サーバー内に同じテーブル定義で既に存在していること
- TTSバックアップの場合、リストアするテーブルは、リストア先サーバー内に存在していないこと
- リストア時に
--datadir
オプションは必須ではないが、付与する場合はリストア先サーバーに合わせないとリストアに失敗する
大前提としては、通常のフルバックアップからのリストアと異なり、オンラインで行われる必要があります。
そして、MySQL5.6以降デフォルトとして定着している innodb_file_per_table
の機能を用いて行われるため、こちらも必須です。
『非TTSバックアップの場合、リストアするテーブルは、リストア先サーバー内に同じテーブル定義で既に存在していること』というのは、実際にリストアしようとしたときに引っかかりそうです。
DROP TABLE してしまったり、ALTER TABLE でテーブル定義を変更してしまったというインシデントを元に戻そうとするには、まずバックアップ時点と同じテーブル定義でテーブルを作成しておかなくてはいけない、ということになります。
本機能を導入するのであれば、日ごろからDB内のテーブル定義を構成管理しておくとともに、MEBによる物理バックアップ以外にmysqldump --no-data
などを使用して、最新のテーブル定義情報もバックアップしておくことをお勧め致します。
制限
- 個別のパーティションを指定して復元できない。パーティションテーブルは全体がリストアされる
- 増分バックアップでは実現できない
- バイナリログ、リレーログ、UNDOログはリストアされない
- 非TTSバックアップの場合の追加制限事項
- 部分的な復元の後、コミットされていないトランザクションからの変更がテーブルに含まれる可能性がある。
- TLRで復元されたテーブルの自動インクリメント値は、バックアッププロセスの最後の値と同じでない場合がある。
- 暗号化されたInnoDBテーブルを部分リストアに含めることはできない。
パーティションテーブルにおいて、個別のパーティション単位で指定してリストアできない、というのは以前からのTTSバックアップからの部分リストアでも同じです。
この点、パーティションテーブルの性質上、非常にニーズがあると思いますので、現時点でTLRの対象外ということは残念ではありますが、今後の機能拡張に期待したいです。
増分バックアップではTLRは使用できない旨、明記されているのですが、この点は新たな発見があったので後述説明したいと思います。
非TTSバックアップの場合の追加制限事項
として2点ほど注意書きがあり、ユーザー視点では非常に気になる内容ですが、いずれも発生条件は明記されておりません。
暗号化されたInnoDBテーブル
に関しては、バージョン8.0.20 までは上記の通りですが、8.0.21のアップデートで、TTSを使用した部分バックアップ・リストアに含められるようになりました。
(注意としては、本記事で取り上げているフルバックアップからの部分リストア
には まだ対応していない、ということになります)
For MySQL Enterprise Backup 8.0.21 and later:
Encrypted InnoDB tables can now be included in partial backups and restores using transportable tablespaces (TTS).
引用元 :MySQL :: MySQL Enterprise Backup 8.0 User’s Guide :: 3 What’s New in MySQL Enterprise Backup 8.0?
フルバックアップからの Table-Level Recovery (TLR) を試してみる
記事タイトルでは8.0.20
としていますが、今回は、MySQL・MEB共に、本稿の執筆時最新GAリリースバージョン8.0.21
を使用して動作を確認してみました。
なお、8.0.20
で試す場合はフルバックアップ実行コマンドの以下の点が異なりますのでご注意ください。
--backup-image
で指定したmbiファイルがカレントディレクトリに作成されてしまうので、絶対パスを指定するようにしてください。--compress
オプションを指定した場合は、TLRを実行時のコマンドには--uncompress
オプションを指定するようにしてください。
また、以降の手順に記載しているバックアップのディレクトリパス・ファイル名・ログイン認証情報などは検証用に用意した環境固有の設定ですので、適宜読み替えてください。
非TTSバックアップ(通常のバックアップ)からTLR
db1スキーマにテスト用のtbl1テーブルを作成し、適当なデータをInsert後、MEBでフルバックアップを取得しました。
1 2 3 4 5 6 7 |
CREATE TABLE db1.tbl1 ( id int UNSIGNED NOT NULL AUTO_INCREMENT, comment varchar(255) NOT NULL, create_dt datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, update_dt datetime ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINE=InnoDB; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# mysql --login-path=localroot -e "SELECT * FROM db1.tbl1" +----+------------------------------------------------------------------------+---------------------+-----------+ | id | comment | create_dt | update_dt | +----+------------------------------------------------------------------------+---------------------+-----------+ | 1 | Fusce consectetur lectus facilisis, auctor orci vitae, molestie purus. | 2020-08-20 15:33:55 | NULL | | 2 | Cras eget magna in augue tincidunt hendrerit. | 2020-08-20 15:33:55 | NULL | | 3 | Phasellus volutpat nulla a ipsum faucibus, nec pulvinar quam gravida. | 2020-08-20 15:33:55 | NULL | | 4 | Duis nec ligula vitae dolor iaculis posuere sed at ipsum. | 2020-08-20 15:33:55 | NULL | +----+------------------------------------------------------------------------+---------------------+-----------+ # mysqlbackup --login-path=meb --backup_dir=/mysql_backup/full --backup-image=fullbackup.mbi --compress backup-to-image ... mysqlbackup completed OK! # ls /mysql_backup/full/fullbackup.mbi /mysql_backup/full/fullbackup.mbi |
tbl1のデータを TRUNCATE TABLE してしまいます。
1 2 |
# mysql --login-path=localroot -e "TRUNCATE TABLE db1.tbl1" # mysql --login-path=localroot -e "SELECT * FROM db1.tbl1" |
ここでTLRの出番です。
この環境ではrootユーザーでMEBを実行していましたので、フルバックアップはすべてrootユーザー所有になっています。オンラインでリストアするため、ファイルオーナーとパーミッションの関係でリストアが失敗しないように、オーナーをmysqlユーザーに変更しておきます。
1 |
# chown -R mysql. /mysql_backup/full |
今回のフルバックアップは単一イメージファイル形式で取得していたので、そのリストアの際にメタデータファイルなどを一時的に展開するディレクトリを指定する必要があり、事前に準備しておきます。(空ディレクトリである必要があります)
1 |
# install -o mysql -g mysql -d /mysql_backup/full/backuptmp |
TLRを実行します。
※rootユーザーで実行すると、バックアップファイルをコピーバックする際にディレクトリとファイルのオーナがrootユーザー所有になってしまい、リストアが失敗してしまいますので、以下コマンドの通りにmysqlユーザーとしてリストアを実行してください。
1 2 3 4 |
# sudo -u mysql mysqlbackup -uroot -pMySQL8.0 \ --include-tables="db1\.tbl" --backup-dir=/mysql_backup/full/backuptmp \ --backup-image=/mysql_backup/full/fullbackup.mbi \ copy-back-and-apply-log |
※マニュアルに記載の通り、--include-tables
オプションにはPOSIX 1003.2標準の正規表現構文が使えます。
また、--exclude-tables
オプションで除外対象のテーブルの指定も組み合わせることができます。
実行ログの出力を、TLRにフォーカスして抜粋しました。
非TTSバックアップからのリストアである旨のメッセージが確認できます。
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 |
MySQL Enterprise Backup Ver 8.0.21-commercial for Linux on x86_64 (MySQL Enterprise - Commercial) Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. 200820 15:41:57 MAIN INFO: Backup Image MEB version string: 8.0.21 [2020-06-16 18:57:08] : : 200820 15:41:57 MAIN INFO: TDE tables and general tablespaces are not supported with non-TTS partial copy-back operation. 200820 15:41:57 MAIN INFO: Skipping binlogs and relaylogs in case of non-TTS partial copy-back operation. : : 200820 15:41:57 MAIN INFO: Copy-back-and-apply-log from compressed image operation starts with following threads 1 read-threads 6 process-threads 1 write-threads 200820 15:41:57 RDR1 INFO: Discarding tablespace for table `db1`.`tbl1`. 200820 15:41:57 RDR1 INFO: Uncompressing db1/tbl1.ibz. 200820 15:41:57 MAIN INFO: MySQL server version is '8.0.21-commercial' 200820 15:41:57 MAIN INFO: Restoring ...8.0.21-commercial version 200820 15:41:57 MAIN INFO: Copy-back operation completed successfully. 200820 15:41:57 MAIN INFO: Source Image Path = /mysql_backup/full/fullbackup.mbi 200820 15:41:57 MAIN INFO: MySQL server version is '8.0.21-commercial' 200820 15:41:57 MAIN INFO: Restoring ...8.0.21-commercial version 200820 15:41:57 MAIN INFO: Creating 14 buffers each of size 65680. 200820 15:41:57 MAIN INFO: Apply-log operation starts with following threads 1 read-threads 1 process-threads 6 apply-threads 200820 15:41:57 MAIN INFO: Using up to 100 MB of memory. 200820 15:41:57 MAIN INFO: ibbackup_logfile's creation parameters: start lsn 2723506176, end lsn 2723507094, start checkpoint 2723506344. 200820 15:41:57 MAIN INFO: This is a non-TTS partial copy-back operation. 200820 15:41:57 MAIN INFO: Loading the space id : 23, space name : /var/lib/mysql/db1/tbl1.ibd. 200820 15:41:57 PCR1 INFO: Starting to parse redo log at lsn = 2723506192, whereas checkpoint_lsn = 2723506344. 200820 15:41:57 PCR1 INFO: Doing recovery: scanned up to log sequence number 2723507094. 200820 15:41:57 PCR1 INFO: Starting to apply a batch of log records to the database.... InnoDB: Progress in percent: 16 33 50 66 83 200820 15:41:57 PCR1 INFO: We were able to parse ibbackup_logfile up to lsn 2723507094. 200820 15:41:57 PCR1 INFO: MySQL server version is '8.0.21-commercial' 200820 15:41:57 PCR1 INFO: Restoring ...8.0.21-commercial version 200820 15:41:57 PCR1 INFO: Last MySQL binlog file position 0 8329, file name mysql-bin.000007 200820 15:41:57 PCR1 INFO: The first data file is '/var/lib/mysql/ibdata1' and the new created log files are at '/var/lib/mysql/' 200820 15:41:57 MAIN INFO: Importing table: `db1`.`tbl1`. 200820 15:41:57 MAIN INFO: Analyzing table: `db1`.`tbl1`. 200820 15:41:57 MAIN INFO: Apply-log operation completed successfully. 200820 15:41:57 MAIN INFO: Backup has been restored successfully. |
ログメッセージで気づいたのですが、TDE(暗号化InnoDBテーブル)だけでなく、一般テーブルスペース内に作成されたテーブルもこの機能では対象外になるというメッセージが出力されています。
※一般テーブルスペースについてはリファレンスマニュアルをご参照ください。
MySQL :: MySQL 8.0 Reference Manual :: 15.6.3.3 General Tablespaces
TLR完了後、期待通り対象テーブルがバックアップ時のデータでリカバリされました。
1 2 3 4 5 6 7 8 9 |
# mysql --login-path=localroot -e "SELECT * FROM db1.tbl1" +----+------------------------------------------------------------------------+---------------------+-----------+ | id | comment | create_dt | update_dt | +----+------------------------------------------------------------------------+---------------------+-----------+ | 1 | Fusce consectetur lectus facilisis, auctor orci vitae, molestie purus. | 2020-08-20 15:33:55 | NULL | | 2 | Cras eget magna in augue tincidunt hendrerit. | 2020-08-20 15:33:55 | NULL | | 3 | Phasellus volutpat nulla a ipsum faucibus, nec pulvinar quam gravida. | 2020-08-20 15:33:55 | NULL | | 4 | Duis nec ligula vitae dolor iaculis posuere sed at ipsum. | 2020-08-20 15:33:55 | NULL | +----+------------------------------------------------------------------------+---------------------+-----------+ |
なお、TRUNCATE TABLE ではなく DROP TABLE した状態でTLRを実行してみたところ、制約事項に記載されている通りの挙動となり、以下のエラーが発生して処理が中断されました。
1 |
200820 14:36:16 RDR1 ERROR: Table '`db1`.`tbl1`' is not created on destination server. Create the table before performing the partial restore operation. |
非TTSバックアップの場合の追加制限事項に書かれている現象が発生するか
前述の通り非常に気になる制約が記載されているので、実際に起こり得るのか確かめたかったのですが…
残念ながらいずれも複数回TLRを実行してみたところでは、以下の事象は発生しませんでした。
- 非TTSバックアップの場合の追加制限事項
- 部分的な復元の後、コミットされていないトランザクションからの変更がテーブルに含まれる可能性がある。
- TLRで復元されたテーブルの自動インクリメント値は、バックアッププロセスの最後の値と同じでない場合がある。
他トランザクションが対象テーブルを更新中かつ未コミットの状況でMEBがTLRを行った場合、当然ロックがかかっていますので、コミットorロールバックされるまで以下の処理が待たされていることは確認しました。
1 |
200820 16:29:37 RDR1 INFO: Discarding tablespace for table `db1`.`tbl1`. |
今回の検証では未解明となり、心残りではありますが、もしこの事象に遭遇する、または発生条件などを具体的に知りたい場合はOracleサポートへお問い合わせいただくようお願い致します。
増分バックアップを適用したフルバックアップからTLR
制約事項には増分バックアップでは実現できない
と明記されていますので、増分or差分バックアップ計画で運用中のDBには利用できないものと読み取れます。
しかし、今回動作を確認してみたところ、厳密に言えば、 増分バックアップをリストア適用したディレクトリフルバックアップからはTLRを実行することが出来ました。
当然のことながら、増分バックアップは変更分のみを含んでいるのでベースのフルバックアップがあってこそのバックアップファイルであり、それ自体から特定のテーブルデータを丸々抽出する、というのは不可能なので、マニュアルの制約事項の通りではあります。
かつ、単一イメージ形式で取得した増分バックアップをリストア適用するには、まず最初にフルバックアップを対象のMySQL上でリストアした後に、そこに対して増分バックアップをcopy-back-and-apply-log
していかなくては実現しません。
There are different ways to use incremental backups to restore a database under different scenarios. The preferred method is to first restore the full backup and make it up-to-date to the time at which the full backup was performed using the copy-back-and-apply-log command (see Example 5.1, “Restoring a Database” on how to do it), then use copy-back-and-apply-log again to restore the incremental backup image on top of the full backup that was just restored:
ところが、ディレクトリフルバックアップの場合は、対象のMySQLにリストアをせずともバックアップディレクトリに対して増分バックアップを適用できるという仕組みがあります。
Incremental directory backups can be restored in a series of copy-back-and-apply-log command, as illustrated above for single-file backups. Alternatively, at anytime after an incremental backup is taken and before the data is restored, you can bring your full backup up-to-date with your incremental backup. First, apply to the full backup any changes that occurred while the backup was running:
引用元 :MySQL :: MySQL Enterprise Backup 8.0 User’s Guide :: 5.1.3 Restoring an Incremental Backup
これを利用して、ディレクトリバックアップ内で増分を適用させ最新の状態にしたフルバックアップからTLRが行うことが出来ました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# mysqlbackup --backup-dir="/mysql_backup/full/" apply-log → ディレクトリフルバックアップに対してログ適用を実行する # mysqlbackup --incremental-backup-dir="/mysql_backup/incremental/incr1" \ --backup-dir="/mysql_backup/full" \ apply-incremental-backup → 増分バックアップ1 をフルバックアップに適用 # mysqlbackup --incremental-backup-dir="/mysql_backup/incremental/incr2" \ --backup-dir="/mysql_backup/full" \ apply-incremental-backup → 増分バックアップ2 をフルバックアップに適用 # chown -R mysql. /mysql_backup/full # rm -rf /mysql_backup/full/backuptmp/* # sudo -u mysql mysqlbackup -uroot -pMySQL8.0 \ --include-tables="db1\.tbl1" --backup-dir=/mysql_backup/full \ copy-back-and-apply-log → TLR により、増分バックアップ2取得時点の db1.tbl1テーブルを部分リストアすることに成功 |
運用上、単一イメージバックアップ形式で取得する必要がある場合はこの方法は採用するのが難しいかもしれませんが、そうでない場合は制約ではなくなることになります。
まとめ
データベースの利用(運用)上、システムによっては「オペレーションミスなどが発生してしまったので特定のテーブルデータをバックアップ時点まで戻したい」という状況・局面は少なからず発生するものかと思います。
そうならないようなシステム作りや運用設計にすることが理想論ではあるものの、どうしても回避できない・防げない、という実状もあるかもしれません。
MySQLのバージョンアップと併せてMEBのバージョンも上げることで、不慮の事故に対するリカバリのためにフルバックアップから抽出できるようになり、対応の柔軟性向上が見込めるのではと思います。
意外といままで出来そうで出来なかった機能として、非常に有益なアップデートだと感じました。
諸々の制約事項があり、もちろん対応できないリカバリケースもあると思いますので、実運用に導入する際にはしっかりとリカバリテストを重ねていただくことを推奨いたしますが、是非 MEB 8.0.20以降で試していただきたいです。