破損したInnodbテーブルを回復する

Innodb MySQLテーブルを使用していて、最高ではないある時点で、ハードウェア、ドライバー、カーネルのクラッシュ、停電、またはMySQL環境でのまれなエラーのいずれかで失敗するとします。 その結果、Innodbテーブルスペース内のいくつかのページに損傷を与えます。



前の記事の1つで、コメントの中で、この状況で何ができるかを尋ねられました。 できる限り簡潔に答えようとします。



それで、今、私たちはこのような状況について話している:

InnoDB: Database page corruption on disk or a failed InnoDB: file read of page 7. InnoDB: You may have to recover from a backup. 080703 23:46:16 InnoDB: Page dump in ascii and hex (16384 bytes): … A LOT OF HEX AND BINARY DATA080703 23:46:16 InnoDB: Page checksum 587461377, prior-to-4.0.14-form checksum 772331632 InnoDB: stored checksum 2287785129, prior-to-4.0.14-form stored checksum 772331632 InnoDB: Page lsn 24 1487506025, low 4 bytes of lsn at page end 1487506025 InnoDB: Page number (if stored to page already) 7, InnoDB: space id (if created with >= MySQL-4.1.1 and stored already) 6353 InnoDB: Page may be an index page where index id is 0 25556 InnoDB: (index “PRIMARY” of tabletest”.”test”) InnoDB: Database page corruption on disk or a failed
      
      







テーブルを復元するにはどうすればよいですか? 原則として、いくつかのタイプの損傷が存在する可能性がありますが、以下では最も一般的なポイントの1つを検討します。 つまり、クラスター化された主キーのページが破損している場合。



この例では、数バイトが置換されるtest.idbファイルが考慮されているため、損傷は十分に中程度です。



同時に、INNODBのCHECK TABLE操作は実際には役に立ちません。 現在破損しているファイルの場合:



 mysql> check table test; ERROR 2013 (HY000): Lost connection to MySQL server during query mysql> check table test; +-----------+-------+----------+----------+ | Table | Op | Msg_type | Msg_text | +-----------+-------+----------+----------+ | test.test | check | status | OK | +-----------+-------+----------+----------+ 1 row in set (0.69 sec)
      
      







最初の実行では、通常モードでテーブルをチェックします。この場合、チェックサムにエラーがある場合(CHECKを実行しても)innodbはクラッシュします。 2番目の場合、innodb_force_recovery = 1を実行します。 ここでも、チェックサムの不一致に関するログに記録が記録されますが、CHECK TABLEはテーブルにすべて問題がないことを示しています。 ご覧のとおり、CHECK TABLEは常に信頼できるわけではありません。



この例では、「損傷」は非常に小さいため、innodb_force_recovery = 1を実行すると、次の結果が得られます。



 mysql> CREATE TABLE `test2` ( -> `c` char(255) DEFAULT NULL, -> `id` int(10) unsigned NOT NULL AUTO_INCREMENT, -> PRIMARY KEY (`id`) -> ) ENGINE=MYISAM; Query OK, 0 rows affected (0.03 sec) mysql> insert into test2 select * from test; Query OK, 229376 rows affected (0.91 sec) Records: 229376 Duplicates: 0 Warnings: 0
      
      







これでMyISAMテーブルのすべてのデータが取得できたので、innodb_force_recoveryオプションなしで再起動した後、古いテーブルを削除し、新しいテーブルをinnodbに変換するだけです。 将来、古いテーブルが必要な場合は、単に名前を変更できます。 2番目の選択肢は、MySQLDumpでダンプし、テーブルをロードし直すことです。 原則として、これはほぼ同じことです。 MyISAMは、次の理由で使用されます。



OPTIMIZE TABLEを使用しないのはなぜですか? これは、innodb_force_recoveryモードでの作業がデータ操作の読み取りモードで実行されるため、データを挿入または削除できないためです(Innodbテーブルを作成または削除することもできます)。



 mysql> optimize table test; +-----------+----------+----------+----------------------------------+ | Table | Op | Msg_type | Msg_text | +-----------+----------+----------+----------------------------------+ | test.test | optimize | error | Got error -1 from storage engine | | test.test | optimize | status | Operation failed | +-----------+----------+----------+----------------------------------+ 2 rows in set, 2 warnings (0.09 sec)
      
      







簡単でしたね?



その後、さらに進んでtest.ibdファイルを編集し、ページヘッダーの1つを完全に削除できます。 innodb_force_recovery = 1を使用している場合でも、CHECK TABLEがクラッシュするようになりました



 080704 0:22:53 InnoDB: Assertion failure in thread 1158060352 in file btr/btr0btr.c line 3235 InnoDB: Failing assertion: page_get_n_recs(page) > 0 || (level == 0 && page_get_page_no(page) == dict_index_get_page(index)) 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_force_recoveryは役に立ちません。これは、さまざまなシステムの場所でデータが破損した場合にのみこれを操作できるためです。 しかし、私たちの場合、これは役に立ちません。



次のエラーが表示されます。



 mysql> insert into test2 select * from test; ERROR 2013 (HY000): Lost connection to MySQL server during query
      
      







破損したデータを回復するために自動プロセスを使用しようとしても、良い結果にはなりません。 したがって、手動回復モードでは、LIMITを使用して一連のコマンドを使用する価値があります。



 mysql> insert ignore into test2 select * from test limit 10; Query OK, 10 rows affected (0.00 sec) Records: 10 Duplicates: 0 Warnings: 0 mysql> insert ignore into test2 select * from test limit 20; Query OK, 10 rows affected (0.00 sec) Records: 20 Duplicates: 10 Warnings: 0 mysql> insert ignore into test2 select * from test limit 100; Query OK, 80 rows affected (0.00 sec) Records: 100 Duplicates: 20 Warnings: 0 mysql> insert ignore into test2 select * from test limit 200; Query OK, 100 rows affected (1.47 sec) Records: 200 Duplicates: 100 Warnings: 0 mysql> insert ignore into test2 select * from test limit 300; ERROR 2013 (HY000): Lost connection to MySQL server during query
      
      







ここで、テーブルの行は、MySQLをクラッシュさせる行に到達するまで、新しいテーブルに転送されます。 これは200〜300行で予想されるため、一連の同様のアクションを使用して問題を解決する価値があります。



これで、テーブル内で破損したデータが見つかりましたが、最大PKを使用する価値があり、他の値を確認します。

 mysql> select max(id) from test2; +---------+ | max(id) | +---------+ | 220 | +---------+ 1 row in set (0.00 sec) mysql> insert ignore into test2 select * from test where id>250; ERROR 2013 (HY000): Lost connection to MySQL server during query mysql> insert ignore into test2 select * from test where id>300; Query OK, 573140 rows affected (7.79 sec) Records: 573140 Duplicates: 0 Warnings: 0
      
      







したがって、30行をスキップしようとしますが、これでは十分ではありません。 80行をスキップし、すべてが正常になりました。 「バイナリ検索」を使用すると、破損したデータの最大量を復元するために何行スキップする必要があるかを理解できます。 行サイズが役立ちます。 したがって、1行あたり280バイトなので、1ページあたり約50行になります。 また、ここでは30行では不十分です。ページテーブルが破損している場合は、少なくともページ全体をスキップする必要があります。 高いBTREEレベルのページが破損している場合、この回復方法を使用するには、さらにページをスキップする必要があります。



たとえば、クラスター化インデックスのルートページが破損している場合、この方法は正常に機能しません。 この場合、 Innodb Recovery Toolkitを使用する価値があります



PS記事の申し込みを受け付けています:)



All Articles