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

MySQL 5.6でスレーブをクラッシュセーフにするには

MySQL Performance Blogの翻訳。MySQL 5.6の新機能である、リレーログのリカバリ機能を使って、クラッシュ誤にスレーブが自動復旧するように設定する方法。

原文
Enabling crash-safe slaves with MySQL 5.6 (English)
原文ライセンス
CC BY-NC-SA
翻訳依頼者
D98ee74ffe0fafbdc83b23907dda3665
翻訳者
D98ee74ffe0fafbdc83b23907dda3665 doublemarket
原著者への翻訳報告
未報告


September 13, 2013 By Stephane Combaudon

スレーブがクラッシュした時にもリカバリ可能(クラッシュセーフ)に設定できるのは、MySQL 5.6のレプリケーション関連の大きな改善の一つだ。しかし、正しくこの機能を使うにはどう設定したらよいのか混乱してしまいがちなので、ここで手順をはっきりさせておこう。

概要

  1. スレーブのMySQLを停止
  2. relay_log_info_repository = TABLErelay_log_recovery = ONをmy.cnf追記
  3. MySQLを再起動して待つ

細かな詳細

クラッシュセーフなスレーブを作りたい時に、なぜ上のような設定をしなければならないのかを完全に理解するために、スレーブがクラッシュした時になぜレプリケーションが切れてしまうのかを確認しよう。

スレーブでは、レプリケーションは2つのスレッドからなっている。1つはIOスレッドで、バイナリログをマスタからローカルのリレーログにコピーする。もう1つはSQLスレッドで、リレーログに書かれたクエリを実行する。それぞれのスレッドが実行しているその時点のポジションは、IOスレッドはmaster.infoに、SQLスレッドはrelay-log.infoに書かれている。

それで今までは特に問題はなかった。ところが、最初の問題として、これらのファイルに書き込みが行われたタイミングと、ディスクへのファイルの書き込みのタイミングが同期していないということだ。サーバがクラッシュした時、ファイルに書き込まれたポジションが正しくないことがあり得るわけだ。MySQL 5.5では、sync_master_info = 1sync_relay_log_info = 1を設定することで、トランザクションごとにこれらのファイルをディスクに同期させて対応することができるようになった。同期はもちろんタダではない(処理時間がかかる)が、ライトバックキャッシュがある場合、この設定は有効だった。

しかしちょっと待ってほしい。sync_master_info = 1sync_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の値と関係なくトランザクションごとにテーブルが更新されるようになってしまう。そのため、このパラメータは設定ファイルから削除しておくとよい。

この記事によって、この素晴らしい機能が皆さんの手助けになるようお手伝いができることを願っている。

次の記事
MySQLのレプリケーション遅延が0とX秒の間を行ったり来たりする5つの理由
前の記事
Gitのリモート操作を5倍から50倍高速化するには

Feed small 記事フィード