ローカルのmysqlが死んだときの備忘録

環境

mysql 5.7.16
macOS 10.12.4
mysqlcheck Ver 2.5.1 Distrib 5.7.16, for osx10.11 (x86_64)

起きたこと

普段通りによくあるrailsのspecを通していたら、全部通らなくなった。エラーを見るとmysqlのコネクションエラー系のやつ。rails sでサーバーは起動できるが、specは通らない。mysqlcheckをしても復旧には至らず。

Mysql2::Error:
  Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)

あぁ、なんか知らないけどlocalのmysqlが死んだんだなと起動してみるも、起動しない

[kenta.miyachi]% sudo mysql.server start                                                                                                                            Password:
Starting MySQL
.. ERROR! The server quit without updating PID file (/usr/local/var/mysql/MKenta.local.pid).

む、なにかおかしいなと思い、エラーログを見てみる。いっぱい出ていた。

2017-04-17 19:57:17 0x70000728a000  InnoDB: Assertion failure in thread 123145422413824 in file trx0purge.cc line 168
InnoDB: Failing assertion: purge_sys->iter.trx_no <= purge_sys->rseg->last_trx_no
InnoDB: We intentionally generate a memory trap.
InnoDB: Submit a detailed bug report to http://bugs.mysql.com.
InnoDB: If you get repeated assertion failures or crashes, even
InnoDB: immediately after the mysqld startup, there may be
InnoDB: corruption in the InnoDB tablespace. Please refer to
InnoDB: http://dev.mysql.com/doc/refman/5.7/en/forcing-innodb-recovery.html
InnoDB: about forcing recovery.
10:57:17 UTC - mysqld got signal 6 ;
This could be because you hit a bug. It is also possible that this binary
or one of the libraries it was linked against is corrupt, improperly built,
or misconfigured. This error can also be caused by malfunctioning hardware.
Attempting to collect some information that could help diagnose the problem.
As this is a crash and something is definitely wrong, the information
collection process might fail.

こんな感じのmessageが出たときは、エラーログでぐぐってフォーラムを見ると良さそう。 ログから見た感じ、このbugっぽさ?でも違う気もする。完全に同じものは見つからず。 https://bugs.mysql.com/bug.php?id=61516

復旧

とりあえず一旦は作業ができれば良いので、DBの復旧を試みる。 innodb_force_recoveryを使ってとりえあえずdataだけdumpする。 値は以下。
1: SRV_FORCE_IGNORE_CORRUPT
2: SRV_FORCE_NO_BACKGROUND
3: SRV_FORCE_NO_TRX_UNDO
4: SRV_FORCE_NO_IBUF_MERGE
5: SRV_FORCE_NO_UNDO_LOG_SCAN
6: SRV_FORCE_NO_LOG_REDO
詳しくは公式にて。
https://dev.mysql.com/doc/refman/5.7/en/forcing-innodb-recovery.html https://dev.mysql.com/doc/refman/5.6/ja/forcing-innodb-recovery.html

[mysqld]
innodb_force_recovery = 3

これで起動だけは出来たので、一旦全部dumpする。 やっていることは

  • 全databaseをdump
  • mysqlテーブル以外を削除して
  • dumpから復元
% mysqldump -u root -p -x --all-databases > alldatabase.dump
% sudo mysql.server stop
% sudo rm -rf `ls -d /var/lib/mysql/* | grep -v "/var/lib/mysql/mysql"`
% mysql -u root -p < alldatabase.dump

とりあえずmysqlが起動するようにはなった。 これでdump復元でうまく行けば無問題、しかしそうは問屋が卸さない

[kenta.miyachi]%  mysql -u root -p < alldatabase.dump                                                                                                                                                                        Enter password:
ERROR 1813 (HY000) at line 5035: Tablespace '`mysql`.`engine_cost`' exists.

既にいるらしい、でもDROP TABLE IF EXISTSを書いているdumpなのでそんなことはないはず。直にCREATE TABLEクエリを投げてみても、同じエラー、DROPもできず。中身を見てみる。

sh-3.2# ls | grep engine
engine_cost.ibd

本来 TableName.frmTableName.ibdがいないと行けないところ、.ibdファイルしかいなかった。これが不整合の元凶なのではないかと。 とりあえず、dumpを取っているのでこいつを消してdumpから復元する。 何度か起きたので繰り返し。無事復旧完了。

.frmと .ibdについて

.frm

テーブルに関するそのデータディクショナリ情報が格納されます。

https://dev.mysql.com/doc/refman/5.6/ja/innodb-frm-file.html

.ibd

テーブル固有のテーブルスペースを持つテーブル (.ibd ファイルに格納) は、MySQL Server を停止させずに個別にリストアできます。間違ってテーブルデータを削除または更新した場合に、この手法を適用でき、DROP TABLE、TRUNCATE TABLE、または DROP DATABASE のステートメントを通じて、実際にはテーブル自体を失うことがありません。

https://dev.mysql.com/doc/mysql-enterprise-backup/3.11/ja/partial.restoring.single.html

復元中に調べていて知ったのですが、壊れたtableを特定できていれば、これで簡単に一部だけリストアできたかもしれない。今回一番の反省でした。

解決

Localなのでかなり雑にやってしまいましたが、.ibdのことを知っていればもう少し作業が楽に進んだかもしれません。 tableが壊れた原因については、別途調査中。今のところ不具合はなし。

ツッコミお待ちしております。

参考

InnoDB が破損し MySQL 全体が不安定に - http://nlogn.ath.cx/archives/001874.html
MySQLのクラッシュバグにご用心 - http://wadahiro.hatenablog.com/entry/20111006/1317927393
MySQLInnoDB破損したときの復旧方法 - http://qiita.com/skouno/items/71174c959abe435223ab
MySQLスキーマ情報に不整合が起こったら - http://accountingse.net/2015/09/943/