mysql starting-crash-recoveryからのリカバリ(DBが削除できない)

  • 1
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

概要

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 を読み取り専用に設定します。