はじめに
RDSのマルチAZやリードレプリカを使っていても、ソースやレプリカがダウンしたときに裏側で何が起きているか説明できますか?
今回は『MySQL運用管理 [実践] 入門』第5章を読んで、レプリケーション環境でのクラッシュ耐性と準同期レプリケーションについて整理しました。
ソースがダウンしたとき
ソースがダウンしたときの挙動は sync_binlog の値によって変わります。
sync_binlog とは
バイナリログをディスクに同期するタイミングを制御するオプションです。
| 値 | 動作 |
|---|---|
0 |
バイナリログのディスクへの同期はOS任せ(非同期) |
1 |
トランザクションがコミットされる前にバイナリログをディスクに同期 |
0,1以外 |
指定した値のコミット数が収集されたあとに同期 |
ダウン時の挙動
| sync_binlog | ダウンの種類 | レプリケーション状態 |
|---|---|---|
0 |
サーバーダウン | エラーの可能性あり |
1 |
サーバーダウン | 継続 |
0,1以外 |
サーバーダウン | エラーの可能性あり |
0 |
mysqldクラッシュ | 継続 |
1 |
mysqldクラッシュ | 継続 |
0,1以外 |
mysqldクラッシュ | 継続 |
sync_binlog=0 の危険性
バイナリログのディスクへの同期はOS任せの非同期です。同期前にサーバーダウンするとバイナリログの最新イベントが消失する可能性があります。レプリケーションエラーまたはデータ不整合になる可能性があります。
sync_binlog=1 の推奨理由
トランザクションがコミットされる前にバイナリログをディスクに同期します。サーバーダウン後に起動してもレプリケーションは継続されます。
ただしディスクへの同期はコストが高く、sync_binlog=0 と比べて10〜20%ほどパフォーマンスが落ちます。安全性とパフォーマンスのトレードオフです。
ソースダウン後の再利用に注意
sync_binlog=1 のソースがサーバーダウン後に再起動しても、そのままレプリカとして利用することはできない可能性が高いです。
理由はクラッシュリカバリにあります。
クラッシュ直後:
バイナリログ → 最新のイベントあり
テーブル → そのデータが存在しない
クラッシュリカバリ:
バイナリログのイベントを使ってデータを復旧
→ レプリカには渡っていないかもしれないデータが出現
→ すでにレプリカがソースに昇格していると データ不整合
対処:ダウンしたソースはフルバックアップからデータリカバリしてからレプリカとして稼働させるのが安全です。
レプリカがダウンしたとき
レプリカにはクラッシュセーフという概念があり、適切に設定することでサーバーダウン後に起動してもレプリケーションを継続できます。
ポジションレプリケーションのクラッシュセーフ設定
[mysqld]
relay_log_info_repository = TABLE # デフォルトはTABLE
relay_log_recovery = ON
sync_relay_log = 1 # MTA方式の場合のみ追加で必要
relay_log_info_repository = TABLE を設定すると、SQLスレッドが適用するイベントとともに mysql.slave_relay_log_info テーブルにバイナリログとポジションを更新します。relay_log_recovery = ON を設定すると、起動時に現在保持しているリレーログを破棄して新しいリレーログを作成します。
MTA方式の場合はさらに sync_relay_log = 1 も必要です。I/Oスレッドがイベントごとにリレーログをディスクに同期します。
GTIDレプリケーションのクラッシュセーフ設定
[mysqld]
relay_log_recovery = ON
GTIDレプリケーションはポジションレプリケーションより考慮する点が少なく、運用が楽です。ソースに接続するときにGTIDセットを送信し、適用済みのGTID以降のイベントを受け取ることが保証されているため、relay_log_recovery = ON にして起動するだけでレプリケーションが復旧されます。
準同期レプリケーション
非同期レプリケーションの問題点
デフォルトのレプリケーションは非同期です。ソースはレプリカにイベントを伝搬したか保証しません。
フェイルオーバーを運用しているときにこれが問題になります。
① ユーザーがソースへコミット → 成功の通知を受け取る
② 直後にフェイルオーバーが発生
③ 昇格したソース(元レプリカ)にそのコミットのデータが存在しない可能性
→ ユーザーには成功と返したのにデータが消える
準同期レプリケーションとは
ソースがイベントをレプリカに渡ったのを確認してからコミットを完了する仕組みです。「レプリカへの適用」ではなく「レプリカへの伝搬」を保証するため、完全な同期ではなく「準同期」と呼ばれます。
① ユーザースレッドがコミット実行
② ソースのバイナリログダンプスレッドがレプリカへイベントを送信
③ レプリカのI/OスレッドがリレーログへI/O
④ レプリカがソースに確認通知を送る
⑤ ソースが通知を受け取ったらコミットを完了する
⑥ ユーザースレッドにセッションが戻る
フェイルオーバーを運用しているならば準同期レプリケーションを有効にした方が良いです。
設定方法
-- ソース
INSTALL PLUGIN rpl_semi_sync_source SONAME 'semisync_source.so';
SET GLOBAL rpl_semi_sync_source_enabled = 1;
-- レプリカ
INSTALL PLUGIN rpl_semi_sync_replica SONAME 'semisync_replica.so';
SET GLOBAL rpl_semi_sync_replica_enabled = 1;
すでにレプリケーションが動作している環境では、レプリケーションの再起動(STOP REPLICA と START REPLICA)が必要です。
主なオプション
| オプション名 | デフォルト | 説明 |
|---|---|---|
rpl_semi_sync_source_timeout |
10000ms | レプリカからの通知を待つ時間。超えると非同期にフォールバック |
rpl_semi_sync_source_wait_for_replica_count |
1 | 通知を受け取るレプリカ数 |
rpl_semi_sync_source_wait_no_replica |
ON | ONだとタイムアウトまで待機。OFFだとレプリカ数が下回った時点で非同期にフォールバック |
タイムアウトの注意点
デフォルトのタイムアウトは10秒です。レプリカが1台の構成でそのレプリカに STOP REPLICA を実行すると、ソースにコミットしたセッションが10秒間待機してしまいます。大量のセッションが詰まり障害を引き起こす可能性があります。
障害時はセッションが滞留してもよいから準同期を優先したい場合は timeout を大きな値にします。コミット処理を優先したい場合は timeout を小さな値にして rpl_semi_sync_source_wait_no_replica を OFF にします。
注意点
- マルチソースレプリケーションとの併用は不可
- カスケードレプリケーション環境ではソース→中間レプリカ間のみ有効にした方が良い(全体で有効にすると遅延が増加)
- フェイルオーバー後にダウンしたソースはレプリカとして再利用できないため破棄が必要
RDSとの対応
RDSのマルチAZはまさにこの準同期レプリケーションを使って高可用性を実現しています。
| 機能 | 仕組み |
|---|---|
| マルチAZ | 準同期レプリケーション+自動フェイルオーバー |
| リードレプリカ | 非同期レプリケーション(遅延あり) |
リードレプリカが非同期なのは、準同期にするとコミットのたびにレプリカへの伝搬を待つため書き込みのレイテンシが増加するためです。
まとめ
-
sync_binlog=1を設定するとサーバーダウン後もレプリケーションが継続される。ただし10〜20%のパフォーマンス低下あり - ダウンしたソースはフルバックアップからリカバリしてからレプリカとして稼働させる
- レプリカのクラッシュセーフはGTIDレプリケーションの方が設定が簡単
- フェイルオーバーを運用しているなら準同期レプリケーションを有効にする
- 準同期のタイムアウト設定はサービスの要件に合わせて調整する