深夜にMySQLが「ストライキ」を起こしたとき
午前2時にSlackからエラー通知が殺到したと想像してください。サーバーにSSHでログインし、MySQLを再起動しようとしても、`Job for mysql.service failed`という短いメッセージが表示されるだけです。`error.log`を確認すると、**InnoDB corruption**、**Assertion failure**、または**checksum mismatch**といった行が並んでいます。この状態ではサービスは起動せず、どのクエリも実行できません。
私の実体験ですが、データセンターの突然の停電により、MySQL 8.0を搭載した50GBのプロダクション用データベースが完全にダウンしたことがあります。ログファイルにはデータページの破損(page corruption)が記録されており、MySQLを起動しても3秒後にはクラッシュする状態でした。このような状況では、通常のバックアップ(もしあれば)からの即時復旧は困難です。唯一の方法は、InnoDBのリカバリメカニズムに介入することです。
なぜ「質実剛健」なInnoDBエンジンが破損するのか?
InnoDBは堅牢なACID特性で知られていますが、外部要因には弱い面もあります。一般的に、データ破損は以下のような理由で発生します:
- ハードウェアの劣化: 重要な
.ibdファイルが保存されている場所にハードディスクの不良セクタが発生した。 - 電源トラブル: Buffer Poolからディスクへデータを書き込んでいる最中に停電が発生し、ファイル構造が不完全になった。
- カーネルエラー: OSのパニックによりファイルシステムのデータ同期が間に合わなかった。
- 強制終了 (Kill -9): killコマンドを多用したことで、トランザクションログが深刻に破損した。
救済ツール:innodb_force_recovery
これは my.cnf ファイルにおける「黄金」のパラメータです。整合性チェックをスキップして、MySQLをリードオンリー(読み取り専用)モードで起動できます。これにより、データを安全に外部へ抽出(dump)することが可能になります。このパラメータには1から6までのレベルがあります。
把握しておくべき復旧レベル:
- 1 (SRV_FORCE_IGNORE_CORRUPT): 破損したデータページを無視してサーバーの稼働を継続する。
- 2 (SRV_FORCE_NO_BACKGROUND): マスター・スレッドの動作を停止させ、パージ(purge)中のクラッシュを防ぐ。
- 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): リドゥログを実行しない。これは最終手段であり、データ紛失のリスクが最も高くなります。
注意: このモードを有効にしている間は、絶対にINSERTやUPDATEを行わないでください。唯一の目的は、mysqldump を使用してデータを救出することです。
データ復活のための5ステップ
以下は、私が1件のレコードも失うことなく50GBのデータを救出した際に使用した手順です。
ステップ 1: データの隔離と生データのバックアップ
バックアップを取らずに、破損したファイルを直接操作しないでください。開始前に、データディレクトリ全体を別の場所にコピーしてください。
# MySQLを停止
sudo systemctl stop mysql
# データディレクトリをバックアップ(通常は /var/lib/mysql)
sudo cp -R /var/lib/mysql /var/lib/mysql_backup_emergency
ステップ 2: リカバリモードの有効化
設定ファイル(/etc/mysql/my.cnf)を開き、[mysqld] セクションに以下の行を追加します:
innodb_force_recovery = 1
MySQLを再起動してみてください。失敗する場合は、サービスが安定するまで値を2、3…と徐々に上げていきます。
ステップ 3: データの抽出(Dump)
MySQLが一時的に安定したら、すぐにデータを .sql ファイルにダンプします。特定のテーブルがひどく破損している場合に備えて、管理しやすいように各データベースを個別にダンプすることをお勧めします。
mysqldump -u root -p --all-databases --routines --triggers > full_dump.sql
エラーが発生してダンプが止まるテーブルがある場合は、--ignore-table パラメータを使用してそのテーブルをスキップし、後で個別に処理します。
ステップ 4: 環境のクリーンアップ
ダンプファイルが取得できたら、MySQLを停止して古いデータディレクトリを削除します。ステップ1でバックアップを取ってあるので、安心して実行できます。
sudo systemctl stop mysql
sudo rm -rf /var/lib/mysql/*
# 設定ファイルの innodb_force_recovery の行を削除するかコメントアウトするのを忘れないでください
mysqld --initialize コマンドで空のデータベースを再初期化するか、サービスを再起動してMySQLに新しい構造を自動生成させます。
ステップ 5: データの再インポート
これで、ダンプファイルからクリーンなシステムにデータを戻します。
sudo systemctl start mysql
mysql -u root -p < full_dump.sql
データを永久に失わないための「痛い教訓」
パニック状態での小さなミスが、データの永久的な喪失につながることがあります。以下のことを覚えておいてください:
- 低いレベルから順に試す: いきなりレベル6に上げないでください。修復不可能なレベルまでデータ構造を破壊する恐れがあります。
- ディスク容量を確認する: 単にディスクが100%いっぱいになったためにMySQLがクラッシュすることもあります。まず最初に
df -hで確認しましょう。 - ログを継続的に監視する: 別のターミナルを開き、
tail -f /var/log/mysql/error.logを実行して、MySQLがどこで苦戦しているのかを正確に把握してください。
治療よりも予防:今後の対策
50GBの障害を経験した後、システムの運用方法を変更しました。今日から取り組むべきことは以下の通りです:
- innodb_flush_log_at_trx_commit = 1 に設定: コミット時に即座にディスクへ書き込まれるようになります。少し低速になりますが、安全性は格段に向上します。
- Percona XtraBackup の使用: このツールを使用すると、テーブルのロックやサービスの停止を伴わずにホットバックアップ(稼働中バックアップ)が可能です。
- I/O 監視: ディスク使用率が80%に達した際や、OSがI/Oエラーを報告した際にアラートが飛ぶように設定します。
データベースのトラブルシューティングは知恵比べです。冷静になり、まずは生データをコピーしてから試行錯誤を始めてください。あなたのシステムが一日も早く正常に戻ることを願っています!

