MySQLにはレプリケーションの仕組みが存在します。
ここではレプリケーションの方式やもう少し内部的な部分を見ていきたいと思います。
前回レプリケーションについて浅く理解するための記事を書いているので、「レプリケーション?」という方はよろしければ前回の記事をご覧ください。
タイミング
MySQLのレプリケーションによってスレーブ側にデータが転送されますよね。一般的にデータ同期のタイミングとして、「同期」、「非同期」という用語が用いられると思います。
MySQLではデータ転送のタイミングは**「完全同期」、「非同期」、「準同期」**のどれかになります。ですがこれらを組み合わせたレプリケーションを構築することが可能です。では順番に簡単にですがまとめていきたいと思います。
完全同期
マスターがトランザクションをコミットすると、アプリケーションに応答を返す前にスレーブ側でもトランザクションをコミットする方式です。
この方式だとリアルタイムにスレーブ側に最新のデータが反映されている状態となるため、耐障害性はあがります。ですがトランザクションの完了が遅れるため性能面で影響が出てくると思われます。
このような処理の流れになるかと思います。(データの書き込みタイミングは間違っているかもしれません。。。)
- アプリケーションからマスターにコミット
- バイナリログおよびデータ(ストレージエンジン)に書き込む
- バイナリログの内容をスレーブに転送
- スレーブ側でリレーログの内容をデータ(ストレージエンジン)に書き込む
- スレーブからマスターに応答
- マスターからアプリケーションに応答
非同期
完全同期とは異なり、マスターがトランザクションをコミットすると、スレーブへのデータ反映を待たずにアプリケーション側に応答を返す方式です。
ここでいうデータ反映とは、スレーブへのバイナリログの転送~スレーブ側へのデータ反映を指しており、マスターは単にバイナリログにイベントを書き込むだけです。後はよしなにスレーブ側が要求することでデータの同期が行われていきます。
この方式ではアプリケーションへの応答時間が短いことがメリットと言えます。ですが、バイナリログの転送が完了していない状態でマスターに障害が発生した場合、「コミットは成功したがデータが存在しない」ことになります。そのためこの方式ではイベントがスレーブに必達することが保証されていないことが問題点として挙げられます。
このような流れになるかと思います。
- アプリケーションからマスターにコミット
- バイナリログとデータ(ストレージエンジン)に書き込む
- マスターからアプリケーションに応答
- バイナリログの内容をスレーブに転送
- スレーブ側でリレーログの内容をデータ(ストレージエンジン)に反映
純同期
簡単にいうと完全同期と非同期の中間に位置し、マスターはコミット後に少なくとも1台のスレーブがバイナリログを受け取るまで待機する方式です。
この方式ではリレーログの内容反映まで待つ必要がなくなるため、完全同期と比べて応答速度が速くなります。またバイナリログの転送まで保証しているため、非同期と比べてデータの完全性も向上しています。
メリットばかりに見えますよね?実はそうではありません。バイナリログを転送したことは保証していますが、データに反映されたかは保証されていないのです。そのためアプリケーション応答時にまだデータの変更が行われていない可能性があります。
- アプリケーションからマスターにコミット
- バイナリログに書き込む
- バイナリログの内容をスレーブに転送
- バイナリログの内容を受け取ったことをマスターに応答
- データ(ストレージエンジン)に書き込む
- マスターからアプリケーションに応答
- スレーブ側でリレーログの内容をデータ(ストレージエンジン)に反映
このような流れになるかと思います。
バイナリログの形式
マスターからスレーブに転送される際のバイナリログ形式は下記のいづれかになります。
- 変更後の行イメージを記録した「行ベース」(Row Based) -> 5.7以降のデフォルト
- 実行されたSQL文を記録した「文ベース」(Statement Based) -> 5.6までのデフォルト
- 上記の混在(Mixed)
Mixedは混在のためRowおよびStatementを見ていきたいと思います。
行イメージ(Row Based)
「行イメージ」は行イメージをバイナリログに記録(エンコード等が走る)し転送する仕様上、「文ベース」と比較するとサーバ間で渡されるデータ量が大きくなります。ですが、 binlog_row_image=minimalとすることで主キーと変更された列のみを記録するようにできるため、転送するデータ量を抑えることができます。また行イメージではSQLが実行されないのでスレーブ側でのトリガーを発生させることができないのは注意点です。
文ベース(Statement Based)
「文ベース」ではSQLを実行する仕様上、利用する関数によってデータの不整合が起こりえます。影響がありそうな関数例として、UUID関数(サーバ固有のIDであるUUIDを取得)やSYSDATE(システム日付取得)などはマスターとスレーブで実行されるタイミングによって異なる値が返ってしまうため問題が発生し得るかもしれません。
GTID
MySQL5.6から加わった機能の一つで、「Global Transaction IDentifier」と言い、トランザクションを一意に識別するためのIDです。そのためバイナリログにもこのGTIDは記録されます。このことからレプリケーションでも活用されており、一度マスターで割り当てられたGTIDはスレーブに渡されても値は変わりません。
ここでGTIDを利用することでレプリケーションがどう変わったのかを見ていきたいと思います。
GTIDを利用していない場合のレプリケーションでは、スレーブでのレプリケーション開始時にマスターのバイナリログファイル名とバイナリログ内のどのポジションからレプリケーションを開始するかを明示的に指定する必要ありました。
ですが、GTIDを利用している場合のレプリケーションでは、スレーブが持っていないGTIDから自動的にレプリケーションを開始すべきトランザクションを見つけることができるようになります。
また下記のようなことも可能になりました。
- マスター障害時の昇格させる最新のスレーブの自動認識
- 複数台のレプリケーション環境でもトランザクションの追跡/比較が容易になる
便利なことだらけですね。
まとめ
GTID凄くないですか?