MySQLデータ復旧:InnoDB Corruption(破損)エラーの実践的な対処法

MySQL tutorial - IT technology blog
MySQL tutorial - IT technology blog

深夜に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

データを永久に失わないための「痛い教訓」

パニック状態での小さなミスが、データの永久的な喪失につながることがあります。以下のことを覚えておいてください:

  1. 低いレベルから順に試す: いきなりレベル6に上げないでください。修復不可能なレベルまでデータ構造を破壊する恐れがあります。
  2. ディスク容量を確認する: 単にディスクが100%いっぱいになったためにMySQLがクラッシュすることもあります。まず最初に df -h で確認しましょう。
  3. ログを継続的に監視する: 別のターミナルを開き、tail -f /var/log/mysql/error.log を実行して、MySQLがどこで苦戦しているのかを正確に把握してください。

治療よりも予防:今後の対策

50GBの障害を経験した後、システムの運用方法を変更しました。今日から取り組むべきことは以下の通りです:

  • innodb_flush_log_at_trx_commit = 1 に設定: コミット時に即座にディスクへ書き込まれるようになります。少し低速になりますが、安全性は格段に向上します。
  • Percona XtraBackup の使用: このツールを使用すると、テーブルのロックやサービスの停止を伴わずにホットバックアップ(稼働中バックアップ)が可能です。
  • I/O 監視: ディスク使用率が80%に達した際や、OSがI/Oエラーを報告した際にアラートが飛ぶように設定します。

データベースのトラブルシューティングは知恵比べです。冷静になり、まずは生データをコピーしてから試行錯誤を始めてください。あなたのシステムが一日も早く正常に戻ることを願っています!

Share: