September 13, 2013 By Stephane Combaudon
スレーブがクラッシュした時にもリカバリ可能(クラッシュセーフ)に設定できるのは、MySQL 5.6のレプリケーション関連の大きな改善の一つだ。しかし、正しくこの機能を使うにはどう設定したらよいのか混乱してしまいがちなので、ここで手順をはっきりさせておこう。
概要
- スレーブのMySQLを停止
relay_log_info_repository = TABLE
とrelay_log_recovery = ON
をmy.cnf追記- MySQLを再起動して待つ
細かな詳細
クラッシュセーフなスレーブを作りたい時に、なぜ上のような設定をしなければならないのかを完全に理解するために、スレーブがクラッシュした時になぜレプリケーションが切れてしまうのかを確認しよう。
スレーブでは、レプリケーションは2つのスレッドからなっている。1つはIOスレッドで、バイナリログをマスタからローカルのリレーログにコピーする。もう1つはSQLスレッドで、リレーログに書かれたクエリを実行する。それぞれのスレッドが実行しているその時点のポジションは、IOスレッドはmaster.info
に、SQLスレッドはrelay-log.info
に書かれている。
それで今までは特に問題はなかった。ところが、最初の問題として、これらのファイルに書き込みが行われたタイミングと、ディスクへのファイルの書き込みのタイミングが同期していないということだ。サーバがクラッシュした時、ファイルに書き込まれたポジションが正しくないことがあり得るわけだ。MySQL 5.5では、sync_master_info = 1
とsync_relay_log_info = 1
を設定することで、トランザクションごとにこれらのファイルをディスクに同期させて対応することができるようになった。同期はもちろんタダではない(処理時間がかかる)が、ライトバックキャッシュがある場合、この設定は有効だった。
しかしちょっと待ってほしい。sync_master_info = 1
とsync_relay_info = 1
を設定したとしても、よくないことは起きるものだ。レプリケーションの情報は、トランザクションがコミットされた後に書き込まれる。すなわち、トランザクションがコミットされた後、かつ、レプリケーション情報が更新される前にクラッシュすると、レプリケーション情報は正しくなくなり、サーバが再起動された後にトランザクションが2回実行されることがあり得る。トランザクションの内容によって、その影響は異なる。レプリケーションには影響がないかもしれないし、切れてしまうかもしれないし、知らないうちに不整合が起きてしまうことすらある。
MySQL 5.6は、レプリケーション情報をファイルではなくテーブルに書き込めるようにすることでこの問題を解決しようとしている(relay_log_info_repository = TABLE
の時mysql.slave_relay_relay_log_info
テーブルが作られ、master_info_info_repository = TABLE
の時mysql.slave_master_info
テーブルが作られる)。考え方はシンプルで、トランザクション内でレプリケーション情報を更新すれば、常にデータと一緒に同期される、ということになる。
疑似コードで書いてみると、以前は以下のように処理されていた。
START TRANSACTION;
-- Statement 1
-- ...
-- Statement N
COMMIT;
-- Update replication info files
それが以下のように動作するようになった。
START TRANSACTION;
-- Statement 1
-- ...
-- Statement N
-- Update replication info
COMMIT;
残念なことに、ここに書いたように単純なことではない。slave_relay_log_info
テーブルはトランザクションがコミットされるのと同時に更新されるため、SQLスレッドに関してはうまく動く。しかし、IOスレッドに関しては、テーブルの更新はトランザクションが実行されるのとは関係ない。それならサーバはどうやってテーブルをいつ更新すべきかを知るのだろうか?
答えはこうだ。sync_master_info
がこれを制御している。デフォルト値は10000で、これはつまり10000トランザクションごとにIOスレッドのポジションが更新される、という意味だ。これはあまりにも大きな値なので、スレーブをクラッシュセーフにするにはよくないのは明らかだ。一つの方法としては、sync_master_info = 1
に設定してしまうことだが、上に書いたように、パフォーマンス上のインパクトが大きい(それがデフォルト値が1でない理由だ)。
ここで、MySQLの再起動は必要ではあるが、relay_log_recovery = ON
にするというよりエレガントな方法がある。この設定によって、サーバが起動してきたとき、常にディスクに同期されているはずのslave_relay_log_info
テーブルから、IOスレッドのポジションがリカバリされる。そのため、スレーブをクラッシュセーフにするため、IOスレッドの情報をテーブルに保存する必要すらなくなる。言い換えれば、master_info_repository = TABLE
の設定は必要ないということだ。
最後に付け加えておくと、relay_log_info_repository = TABLE
を設定すると、sync_relay_log_info
の値と関係なくトランザクションごとにテーブルが更新されるようになってしまう。そのため、このパラメータは設定ファイルから削除しておくとよい。
この記事によって、この素晴らしい機能が皆さんの手助けになるようお手伝いができることを願っている。