Yakstは、海外の役立つブログ記事などを人力で翻訳して公開するプロジェクトです。
約11年前投稿 修正あり

InnoDBログシーケンス番号(LSN)を進める方法

MySQL Performance Blogの翻訳。InnoDBのログシーケンス番号が不整合を起したとき、gdbを使ってメモリ上のLSNを強制的に書き換える方法。

原文
How to move the InnoDB log sequence number (LSN) forward - MySQL Performance Blog (English)
原文ライセンス
CC BY-NC-SA
翻訳依頼者
D98ee74ffe0fafbdc83b23907dda3665
翻訳者
D98ee74ffe0fafbdc83b23907dda3665 doublemarket
原著者への翻訳報告
未報告


この記事では、InnoDBログシーケンス番号が未来のものになってしまった時について焦点を当ててみる。

前書き : InnoDBログシーケンス番号って何?

ログシーケンス番号(LSN)は、InnoDBによってあちこちで使われている重要なパラメータだ。最も重要な役割としては、クラッシュリカバリとバッファプールのパージの制御だ。

内部的には、InnoDB LSNカウンタが巻戻ることはあり得ない。そして、InnoDBがredoログに50バイト書き込めば、LSNも50バイト進む。そんな風に、LSNをメガバイト単位、ギガバイト単位で数えることもできる。

ここで問題 : LSNが未来を指している!

以下のように設定し、

innodb_force_recovery=6

データを操作するクエリを実行したとする。

例えば、バックアップのためにmysqldumpでダンプした後、不整合のあるテーブルを削除したとすると、InnoDBはibdata1に間違ったLSNを書き込んでしまい、MySQLサーバを再起動するとエラーログにエラーメッセージが書き込まれてしまうだろう。

120323 4:38:52 InnoDB: Error: page 0 log sequence number 6094071743825
InnoDB: is in the future! Current system log sequence number 10000.

解決法 : LSNを変更する方法

通常、LSNを修正する最も安全な方法は、(LSNが正しい位置に戻るために)必要な量のデータをinsertあるいはdeleteすればよい。

しかし、LSNが数TB分もあった場合はどうするべきか? いくつかの方法がある。

  1. バックアップデータを使う
  2. 全テーブルをMyISAMに変換し、ibdata1とib_logfile*を削除し、サーバを再起動してから全テーブルをInnoDBに変換し直す
  3. mysqldumpでのダンプとリストア
  4. 巨大なデータベースを持っている場合は、黒魔術を使う

1から3の方法が使えない場合に正しいLSNを得るためには、InnoDBのファイルを直接いじるか、mysqldのメモリ空間を変更するという、やや危険な方法をとるしかない。

  1. debuginfoパッケージがインストールされていることを確認する
  2. 作業中に(絶対!)クエリが実行されないようにする。さもないとLSNが更新されてしまう。
  3. gdb -p `pgrep -x mysqld`
    gdb) p log_sys->lsn
    $1 = 12300
    (gdb) set log_sys->lsn = 12300000;
    Invalid character ';' in expression.
    (gdb) set log_sys->lsn = 12300000
    (gdb) c
    
  4. mysqldをシャットダウンする。通常のシャットダウンができるはず。

  5. エラーログに正しいLSNが記録されているか確認する。

  6. mysqldを起動し、正しいLSNが表示されるか確認する。

    LOG
    ---
    Log sequence number 12300000
    Log flushed up to 12300000
    Last checkpoint at 12300000
    
  7. 何かをinsertしてみて、LSNが変わるのを確認する。

その他の問題 : LSN変更後にデータベースが壊れるのを回避する

  • もちろん、MySQLの内部構造に立ち入った方法なので、将来のInnoDBバージョンではうまくいかない可能性がある。このブログ記事を書くためには、5.5.32-rel31.0-549.preciseのメモリ上のLSNを書き換えてみた。この方法があなたの使っているバージョンで動くかどうかは、ステージング環境で確認して欲しい。
  • また、もしサーバがクラッシュするとリカバリできないことになるので、運用中のサーバのLSNをオンラインで変更するのは非常によろしくないことだ。
  • あるタイミングにおいて、MySQLサーバは同じLSNを持ってアイドル状態でなければならない。Log sequence numberLog flushed up toLast checkpoint atはアイドル状態のサーバでは全て一致する。もしサーバがアイドル状態でなく、SHOW MASTER STATUS\Gの出力に変化がないなら、SET GLOBAL innodb_fast_shutdown=0を実行して、MySQLを再起動しよう。
  • MySQLサーバの再起動は、LSNの変化をトランザクションログに書き込む。
  • 手順の前に、システムが安定状態でなければならない。サーバの再起動時のmysqldのクラッシュは、データの破損を招く。再起動のプロセスが素早く終わること、エラーログを確認してリカバリプロセスが実行中でないことを確認しよう。

このような不整合は何から引き起こされるのだろうか?

mysqldがクラッシュすると、再起動時にInnoDBではクラッシュリカバリが実行される。InnoDBのクラッシュリカバリでは、ディスク上にあった最後のチェックポイントから、Log flushed up toのポジションまでトランザクションログを適用していく。Log flushed up toポジションが存在しないポジションだった場合、InnoDBは過去のイベントを適用しようとする。これは、トランザクションログはリングバッファのルールに従っているからだ。変更の前にトランザクションログを再作成すれば、追加の変更を適用することもできる。mysqldのクラッシュ時も重大なデータ欠損が起きないようにすることさえできる。

最後の忠告 : gdbでメモリを変更する際は常にバックアップを取ること。特に、そのバージョンのMySQLでテストしたことないことをやるときは。

次の記事
Gitのリモート操作を5倍から50倍高速化するには
前の記事
これが私の好きなMySQL 5.6の新機能。あなたの好きな機能は?

Feed small 記事フィード

新着記事Twitterアカウント