みなさんこんにちは。
この記事がアップされる頃にはFGO贋作イベは終わっているでしょう、ディーネット谷口です。
さて今回は、MySQLの準同期レプリケーションの仕組みについてお話したいと思います。
レプリケーションは使っているという方は多くいらっしゃると思いますが、
実は内部構造はしっかりと把握していないという事があると思います。
そもそもレプリケーションとは?
レプリケーションは1つのMASTERサーバー、1つ以上のSLAVEサーバーの構成で構築出来ます。
一般にレプリケーションは以下のようなシステムになっています。
MASTER・SLAVEともに参照クエリを実行できます
MASTERに更新クエリを実行すると、MASTER・SLAVE両方に更新が適用されます
SLAVEに更新クエリを送信出来ません(送信するとMASTERとデータの齟齬が生まれてしまうから)
レプリケーションの動作
1.MASTER側で更新クエリを実行
2.MASTER側からSLAVE側にSQLバイナリログを転送
3.SLAVE側で更新クエリを実行
※手順「2.」の途中でMASTERがクラッシュした場合、MASTERとSLAVE間でデータの齟齬が生じます。
準同期レプリケーションとは?
準同期レプリケーションは、サーバーの可用性を向上させる技術の一つです。
準同期レプリケーションを設定すれば、更新中にMASTERがダウンした場合のリスクを大幅に軽減できます。
MySQLバージョン5.5で、準同期レプリケーション機能が追加されました。
※以前までのレプリケーションは非同期レプリケーションと区分けされるようになっています。
準同期レプリケーションの動作
MASTER側からSLAVE側にSQLバイナリログを転送
SLAVE側からMASTER側に「更新出来ますよ」サインを返信
MASTER側・SLAVE側それぞれで更新クエリを実行
※本手順では、最初にSQLバイナリログを送信しているため、MASTERとSLAVE間で齟齬が生じる可能性は非常に低いです
目次
レプリケーションの仕組み
1.MySQL サーバのマスタに書き込みが行われると、マスタは更新情報を バイナリログ (binlog) に書き込みます。
2.スレーブでは I/O スレッドが作成され、マスタに接続要求を投げます。
3.マスタはバイナリログを送信するためにスレッドを作成してスレーブに送信します。
4.スレーブはマスタから受信した情報をリレーログと呼ばれるローカルファイルに書き込みます。
5.続いてスレーブは SQL スレッドを起動して、リレーログのクエリを実行します。
しかし、この方式ではマスタが関与するのは binlog に書き込む所までになります。
それ以降は知ったこっちゃないということになります。
つまり、スレーブが更新情報を取得する前にマスタがダウンするとデータの不整合が起こります。
準同期レプリケーションの仕組み
MySQL 5.1のレプリケーションでは、
これらのスレッドは完全にマスター上で実行中のトランザクションとは独立して動いている。
つまり非同期である。
ところが、MySQL 5.5では次の図で示すように「トランザクションのCOMMITの応答がクライアントへ返る前に、
スレーブへバイナリログが到達している」ということを保証するモードが追加された。
これが準同期レプリケーションである。
準同期レプリケーションではトランザクションがCOMMITする前にスレーブのI/Oスレッドが、
マスターからバイナリログを受け取ってackを返します。
つまりI/Oスレッドはマスターと同期しています。
だが、その更新を実際に適用する、すなわちリレーログをSQLスレッドがテーブルへ反映するのは非同期で行われます。
I/Oスレッドは同期処理、SQLスレッドは非同期処理、従って準同期という事になります。
準同期レプリケーションの確認方法
準同期レプリケーションの場合、masterでcommitした時にbinlogを書いてslaveに転送し、
受け取ったslaveのどれか 1台からackが返ってきて初めてcommitが成功してトランザクションが解放されます。
今までの非同期レプリケーションの場合 ackとか無いのでbinlogに書いてしまえばcommitは成功します。
slaveを1台だけぶら下げてあえてio_threadを停止させた後にmasterでcommitした場合どうなるか試してみましょう。
非同期レプリケーションの場合
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
(slave側でstop slave)
mysql> insert into t1 values (6);
Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec) ←一瞬でcommit成功
準同期レプリケーションの場合
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
(slave側でstop slave)
mysql> insert into t1 values (10);
Query OK, 1 row affected (0.00 sec)
mysql> commit;
★固まった → slave側でstart slave
Query OK, 0 rows affected (5.88 sec)
動作結果
commitをした際にslaveからのackが返ってこないので、
rpl_semi_sync_master_timeout(単位はmsec)の間はcommitが待ちになりました。
デフォルトだと10秒に設定してあるので、今回はその秒数以内にslave側でio_threadを再開したので、
その瞬間にbinlogを受け取ってackを返してくれます。
ちなみにrpl_semi_sync_master_timeoutを過ぎるとロールバックするのではなく commitに成功して終わります。
非同期と準同期では、負荷などが変わるのでシステム要件によって使い分けをするようにしましょう。
他のレプリケーション方法などについてはまたの機会にご紹介出来ればと思います。