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

#概要
[Percona xtrabackupを使用したバックアップ、リストアでハマったこと](http://qiita.com/takemotto/items/ab78b692bca8e9838de8)
で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」を実行する](http://takahitokikuchi.poitan.net/2012/10/04/mysql%E3%81%8C%E4%BD%95%E5%BA%A6%E3%82%82%E3%80%8Cstarting-crash-recovery%E3%80%8D%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B/)

しばらく様子を見てもいっこうに状況が変わらないため、一旦kill -9でプロセスをmysqlプロセス強制停止。
my.cnfの [mysqld] に「innodb_force_recovery」を設定して強制リカバリモードで起動してみることに。
innodb_force_recoveryは1から6まで設定でき、1から順に実行して、復旧しなければ2といった感じで徐々に数値を上げてrecoveryを行うようだ。
強制リカバリの処理内容は以下のページを参照
[InnoDB のリカバリの強制的な実行](https://dev.mysql.com/doc/refman/5.6/ja/forcing-innodb-recovery.html)

今回は、「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 を読み取り専用に設定します。
```