この記事で
症状
変更
原因
解決策
- 初期ログファイルをリカバリする
- データを再びロードする
- MyISAMに転換する
- 仕事テーブル
リファレンス
このファイルはOracle Support’s Rapid Visibility (RaV)で発信するから、独立した技術テストに制約されていない。
適用範囲:
MySQLサーバ4.0以降
この記事では、すべてのプラットフォームに適用する
症状
リカバリしてみた後にInnoDBを使うと、以下のエラになる。
ERROR
‐‐‐‐‐ ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
120207 13:30:29 InnoDB: Error: page 573441 log sequence number 22 697197707
InnoDB: is in the future! Current system log sequence number 5 2916730276.
InnoDB: Your database may be corrupt or you may have copied the InnoDB
InnoDB: tablespace but not the InnoDB log files. See
InnoDB: http://dev.mysql.com/doc/refman/5.5/en/forci ng‐recovery.html
InnoDB: for more information.
変更
このトラブルは以下の変更をした後に起こる:
- もしInnoDBログファイルが削除されて、それにこれらのトランザクションが削除する前に起こる(どんなトランザクションでも外力がないままに自力で起これない。大体の場合は以下のトランザクションで引き起こした):
- ハードウェアサーバシャットダウン
- Mysqldプロセスがkill ‐9あるいはほかの原因で中止する。
- データがホットバックアップからリカバリする。
- innodb_fast_shutdown = 2を使うときに,MySQLが閉められる。
- MySQLがInnoDBが存在していないあるいはほかのバックアップからリカバリする。
- MySQLが整合性のないバックアップからリカバリする。
- innodb_force_recovery = 6の場合に,MySQLが起動される。
原因
根本的な原因はInnoDBログファイル(redoログ)とデータファイルが同期していない。
InnoDBに変更があったら、データファイルに書き込む前に、まずはログファイルに書き込む。innodb_fast_shutdown = 0か1 (ディフォルト)が正常に閉められる間に、InnoDBはすべてのページが上書きされるから、すべてのページより遅いチェックポイントを設定する。そしてサーバがシャットダウンする前にチェックポイントをつくる。innodb_fast_shutdown = 2の場合に今のがチェックポイントとされるが、どんなページも今の位置にフラシュしないから、シャットダウンリカバリが必要としている。シャットダウンリカバリがこのエラを引き起こさないが、もしログファイル削除されたら、シャットダウンしたあとこれらを削除するようにエラになる。
InnoDBがディスクからページを読み取り、エラについての情報を発信する時に、ページとログファイル位置と比べるようになる。
ログ位置は今の位置に超えたページに対してシャットダウンリカバリを実行しない。これはトラブルが解決されるまでまともに運用できないと意味する。
もし原因はinnodb_force_recovery = 6でMySQLを起動したことと関係あるなら、これははっきりとMySQLエラログに映される。詳しい情報はStarting InnoDB on a Corrupted Databaseに参考してください。
解決策
- もしそれらをまだ持っていれば、バックアップあるいはデータファイルをコピする位置から初期ログファイルをリカバリする。このトラブルが起きたが何の変更をしていない場合に、全てのデータをリカバリする。
- mysqldumpでデータをバックアップする。すべてのInnoDBテーブルのib_logfile*, ibdata*, *.ibd, 及び*.frmを削除する;ダンプからInnoDBテーブルを再構造する。この場合にはデータをなくしたことがよくある。
- すべてのInnoDBテーブルをMyISAMに変更する。すべてのib_logfile*, ibdata*を削除して、MySQLを再起動して、テーブルをInnoDBに格納する。効果は解決策2と同じようになるから、データもよくなくす。
- InnoDBストレージエンジンで”work table”を作成する。大きさはログファイルの大きさと同じようになる。 データをインサートして、仕事テーブルを削除する。これもデータをなくすことを引き起こす。
策2と3は大体同じで、大事なのは残したInnoDBテーブルがないということ。例えば、InnoDBテーブルをMyISAMに転換する。その中には具体的なInnoDB情報がなくして、mysqldumpを使っているテーブルが残されたテーブルはバックアップされて、再起動したらリカバリできる。
策2., 3., 及び4で選んで、人々がよく選択でいるのは一番短い時間に完成できる策である。けど、策4には未検知なトラブルがあるかも知れないので、ほかの二つ方法を選んでください。
- 初期ログファイルをリカバリする
これはログファイルを使っているすべてのトランザクションから時間を計算するから、後のデータをなくなる方法より増しだと思う。エラが起こり、データが変更された場合を除き、この選択肢を優先的に使ってください。こうすれば損害を最低に控える。
- データを再びロードする
すべてのInnoDBファイルを初期化して、すべてが元のように戻ることを確保してください。
ステップは:
- すべてのInnoDBデータをダンプする。mysqldump あるいは SELECT … INTOOUTFILEで完成してください。Schema情報を含んでください。例えばテーブル定義とトリガー。別のストレージエンジンを使うとき必要は内容。
- MySQLを中止する。
- すべてのInnoDBファイルを削除する。ファイルの位置と名前がMySQL セットで決める;ディフォルトはすべてのファイルがdatadirでMySQLデータパスに配置される。以下のファイルを削除してください:
- 共有テーブルスペースファイル(各ファイルはディフォルトでibdata*と呼んでいる)。
- InnoDBログファイル(redoログ) ib_logfile*。
- InnoDBテーブルのテーブル定義ファイル、*.frm。
- InnoDBテーブルのトリガーファイル (*.TRN と *.TRG)。
- InnoDBデータファイル*.ibd。
- MySQLを起動して、InnoDBが再び初期化されることを待つ。
InnoDBが共有テーブルスペースとログファイルを作成したら、MySQLエラログで見られる。
- InnoDBファイルのデータとschemaを再びロードする。
- MyISAMに転換する
InnoDBテーブルをMyISAMに転換し、InnoDB共有テーブルスペースとログファイルを削除した同時に、mysqldumpがそれをリカバリすることを阻止してください。
特にMySQL 5.1、InnoDBプラグインとMySQL 5.5を使った場合に、高速インディクスでこのプロセスをより加速する。
ステップは:
- 各InnoDBテーブルに対して、テーブルをMyISAMオプションに転換する:
もし5.1あるいはMySQL 5.5にInnoDBプラグインを使ったら、テーブルからすべてのsecondaryインディクスを削除してください。
Secondaryインディクスを削除することはオプティマイザだから、この選択肢は必要じゃない。大きいなテーブルに対して、著しい性能向上をもたらす。
例えば:
ALTER TABLE t1
DROP INDEX va l1,
DROP INDEX val2,
ENGINE=MyISAM;
- MySQLを中止する.
- すべてのInnoDBファイルを削除する。ファイルの位置と名前はMySQLセットで決める;ディフォルトはすべてのファイルはdatadirでMySQLデータパスに配置される。以下のファイルを削除してください:
- 共有テーブルスペースファイル(各ファイルはディフォルトでibdata*と呼んでいる)。
- InnoDBログファイル(redoログ) ib_logfile*。
- MySQLを起動して、InnoDBが再び初期化されることを待つ。
InnoDBが共有テーブルスペースとログファイルを作成したら、MySQLエラログで見られる。
- 各テーブルに対して:
- テーブルをInnoDBに転換して、そしてステップ1削除された唯一インディクスを追加する。この段階で操作したいくないがであれば、唯一インディクスが高速インディクスで作成できないから、後で追加するのはテーブルが再構造することに導く:
ALTER TABLE t1
ADD UNIQUE ( val1),
ENGINE=InnoDB;
- secondaryインディクスを再びテーブルに追加する:
ALTER TABLE t1
ADD INDEX (v al2);
- 仕事テーブル
この方法は今のログ順列を未来が報告するシリアル番号に追加するので、二つのシリアル番号の違いによって、インサートする必要があるデータ量を見積もることができる:
120207 13:30:29 InnoDB: Error: page 573441 log sequence number 22 697197707
InnoDB: is in the future! Current system log sequence number 5 2916730276.
番号はSHOW ENGINE INNODBSTATUS (参见Document 1326051.1如何在计算中使用日志序列号的示例)でのLOG部分のフォーマットと一緒だが、両者の違いは:
data required = ((22 ‐ 5) * 4 * 1024 * 1024 * 1024) + (697197707 ‐ 2916730276)
= 70794911463 bytes
つまり66GBくらい。
注意:InnoDBプラグインがあるMySQL 5.1あるいはMySQL 5.5を使えば,ログシリアル番号は2つの32ビット数じゃなく、64桁の番号を含む。
この方法を実行したいであれば、テーブルを作成して、システムログシリアル番号がログシリアル番号と同じようになるまで、データを停ずにインサートする。完成したら、テーブルを削除する。
これはlinuxのスクリプト,ログシリアル番号より20Mほど上回る:
#!/bin/sh
mysql ‐uroot test <<EOF
DROP TABLE IF EXISTS te mp_advance_lsn;
CREATE TABLE temp_advance_lsn (col1 TE XT) ENGINE=InnoDB;
INSERT INTO temp_advance_lsn VALUES (REPEAT(‘0123456789’ , 1000));
INSERT INTO temp_advance_lsn SELECT * FROM temp_advance_lsn;
INSERT INTO temp_advance_lsn SELECT * FROM temp_advance_lsn;
INSERT INTO temp_advance_lsn SELECT * FROM temp_advance_lsn;
INSERT INTO temp_advance_lsn SELECT * FROM temp_advance_lsn;
INSERT INTO temp_advance_lsn SELECT * FROM temp_advance_lsn;
INSERT INTO temp_advance_lsn SELECT * FROM temp_advance_lsn;
INSERT INTO temp_advance_lsn SELECT * FROM temp_advance_lsn;
INSERT INTO temp_advance_lsn SELECT * FROM temp_advance_lsn;
INSERT INTO temp_advance_lsn SELECT * FROM temp_advance_lsn;
INSERT INTO temp_advance_lsn SELECT * FROM temp_advance_lsn;
DROP TABLE temp_advance_lsn;
EOF
mysql ‐uroot ‐e ‘SHOW ENGINE INNODB STATUS;’ \
| sed ‘s/\\n/\n/g’ \
| grep ‘^Log sequenc e’
ログシリアル番号より20M以上に超えるようにしたいであれば、テーブルを削除することじゃなく、テーブルをTRUNCATEしてください。LSN目標に達成するまで、データと前にTRUNCATEされたプロセスを追加することを繰り返す。これはより少ないデータでより多くデータを高速で作成した例である。
作成したら、場合によって一番効率が高いデータブロックにきめる。
Leave a Reply