#概要
Percona xtrabackupを使用したバックアップ、リストアでハマったこと
でxtrabackupを使用したリストア処理中にDISCARD TABLEスペース処理で失敗し、
そこから「starting-crash-recovery」がとなった時の対応を書きます。
今回実行したリストアスクリプトは、ざっくり
- DROP TABLE(DBの全テーブル)
- CREATE TABLE(mysqldump --no-dataで出力されたものを使ってインポート)
- ALTER TABLE テーブル名 DISCARD TABLESPACE ※外部キー制約などでここで失敗して、リストアがこけていた。
- ALTER TABLE テーブル名 IMPORT TABLESPACE
という処理をしていました。
#環境
$ cat /etc/redhat-release
CentOS release 6.4 (Final)
$ mysql --version
mysql Ver 14.14 Distrib 5.6.12, for Linux (x86_64) using EditLine wrapper
#やったこと
まず、mysqlを再起動しようと思い、sudo service mysql restart を実行したところ以下のエラーが出力された。
ERROR! MySQL server PID file could not be found!
mysqld.logを確認すると同じテーブルに対し何度もを「starting-crash-recovery」を実行している状態だった。
事象としては、以下の記事と同じものだった。
MySQLが何度も「Starting crash recovery」を実行する
しばらく様子を見てもいっこうに状況が変わらないため、一旦kill -9でプロセスをmysqlプロセス強制停止。
my.cnfの [mysqld] に「innodb_force_recovery」を設定して強制リカバリモードで起動してみることに。
innodb_force_recoveryは1から6まで設定でき、1から順に実行して、復旧しなければ2といった感じで徐々に数値を上げてrecoveryを行うようだ。
強制リカバリの処理内容は以下のページを参照
InnoDB のリカバリの強制的な実行
今回は、「innodb_force_recovery=4」でmysqldが起動することを確認した。
「ALTER TABLE テーブル名 DISCARD TABLESPACE;」の途中で止まっていたので、再度実行してみる。(当然、InnoDB以外のテーブルは失敗するがとりあえずスキップ)
「ALTER TABLE テーブル名 IMPORT TABLESPACE;」を実行してみる。
ERROR 1036 (HY000): Table 'area' is read only
読み取り専用になっている?
どうやらIMPORT TABLESPACEもできないようになっている。
では一旦、元のリストアスクリプトを実行してみようということで、リストアを開始するもこける。。
それならとDROP DATABASEで今回リストアに失敗したDBを削除し、再度リストアしてみてみようということに。
mysql> drop database <DB名>
ERROR 1051 (42S02): Unknown table 'DB名.テーブル名,DB名.テーブル名,DB名.テーブル名'
試しにCREATE TABLEしてもダメ
mysql> create table testtest (id integer);
ERROR 1036 (HY000): Table 'testtest' is read only
DBも削除できない。。。原因はこれだった。
MySQL 5.6.15 の時点では、4 以上の innodb_force_recovery 設定は InnoDB を読み取り専用モードにします。
さきほどinnodb_force_recoveryを「4」で起動していたことが原因だった。
innodb_force_recoveryを外して、mysqlを再起動し、再度DROP TABLEをリトライ。
ERROR 2013 (HY000): Lost connection to MySQL server during query
困った。DBが削除できない。
次に前日に取得していたバックアップを展開し、データディレクトリ(/var/lib/mysq/DB名)を差し替えてみることに。
mysqlは起動するが、select文を実行すると以下のエラーが出力される。
ERROR 1016 (HY000): db not initialized
いよいよやることがなくなって、最後の手段としてinnodb_force_recoveryの「5」を試してみる → 変化なし。
innodb_force_recoveryの「6」 を試してみる。mysqld.logから以下のログ出力が消える。
InnoDB: DISCARD flag set for table '"DB名"."テーブル名"', ignored.
ここで再度DROP DATABASEにトライ。
mysql> drop database <DB名>;
ERROR 1010 (HY000): Error dropping database (can't rmdir './<DB名>', errno: 39)
上記エラーは、データディレクトリ(/var/lib/mysq/DB名)配下のファイルを主導で削除すると良いらしい。
$ rm -f /var/lib/mysq/DB名/*
mysql> drop database <DB名>;
Query OK, 0 rows affected (0.00 sec)
念願のDBの削除完了!
これでDBが削除できたため、リストアを再実行して復旧完了。
#まとめ
そもそも検証が十分されたいなかったことを反省しています。改めて検証が大事だと痛感。
あとは誤算だったのは、DBが削除できないという想定外の事象に遭遇したこと。
最終的には、innodb_force_recoveryの「6」という力技?でDBを削除できるようになった。
しかしこれは最終手段として使うと良いのかもしれません。
innodb_force_recoveryの「6」のドキュメントは以下。
6 (SRV_FORCE_NO_LOG_REDO)
リカバリに関連した Redo ログのロールフォワードを実行しません。
この値を指定すると、データファイルが永続的に破損する場合があります。
データベースページを廃止された状態のままにし、
それによって B ツリーやその他のデータベース構造にさらに多くの破損が発生する可能性があります。
MySQL 5.6.15 の時点では、InnoDB を読み取り専用に設定します。