はじめに
MySQL におけるレプリケーションでは、システムの要件によって以下のように様々な構成を取ることができます。
今回はその中でも、以下のように双方向にレプリケーションをおこなっている(マルチ(デュアル)マスター・循環レプリケーション)構成について、その危険性について改めて確認していきたいと思います。
なお、弊社では双方向(循環)レプリケーション構成の利用については推奨しておりません。
双方向(循環)レプリケーション構成が利用されるタイミングについて
以前の記事でも紹介していますが、この構成が利用されているケースでは、主に以下のような目的で採用されていることが多いかと思います。
- Active/Standby : 普段はどちらかのサーバーのみ利用して、片方がダウンした場合に切り替えて処理を継続できるようにする
- Active/Active : 処理を両方のサーバーに分散させて実行する
どちらの場合でも、利用する際には注意しなければならない点があるため、それぞれ説明させていただきます。
Active/Standby 構成時の注意点
クライアント処理を切り替える際のレプリケーション I/O スレッドの遅延
もしデフォルトの非同期レプリケーションであった場合、ソースのバイナリログの情報を受け取ってレプリカのリレーログに書き込むレプリケーション I/O スレッドは、ソースのバイナリログと同期していることが保証されていません。
そのため、障害が発生したタイミングによっては、以下のようにソースでコミットされていても、レプリカに届いていない更新情報が発生してしまい、クライアント処理を切り替えた際にデータ不整合となる可能性があります。
こうした事態を避けるために、まずは準同期レプリケーションを使用しておく必要があります。
準同期レプリケーションの場合、ソースのバイナリログの情報がレプリカのリレーログに書き込まれていることが保証されるため、上記のようなデータ不整合は発生しなくなります。なお、準同期レプリケーションではクライアントにコミットを返す前にリレーログへの書き込みを待つ必要があるため、ソースの応答速度は非同期レプリケーションより遅くなりやすい点に注意してください。
クライアント処理を切り替える際のレプリケーション SQL スレッドの遅延
また、準同期レプリケーションを使用しているケースでも、以下のようにリレーログに書き込まれた内容がレプリカに反映されるまでに時間がかかっていると、クライアント処理を切り替えた際にソースで更新されたデータがまだ反映されていない可能性があります。
このような事態を避けるために、クライアント処理を切り替える際にはレプリケーション SQL スレッドの遅延が発生していないかを確認する必要があります。
レプリケーション SQL スレッドがリレーログの内容を反映しきっているかどうかについては、SHOW REPLICA STATUS の Seconds_Behind_Source
から確認することができます。
1 2 3 4 5 |
mysql> SHOW REPLICA STATUS\G (...) Seconds_Behind_Source: 60 (...) 1 row in set (0.00 sec) |
この値が 0
であればリレーログとの差分がないことを示しているため、クライアント処理を切り替える際にはこの値を確認してから実施するようにしてください。
Active/Active 構成時の注意点
同じテーブルを更新した際のデータ不整合(auto_increment カラムへの INSERT)
MySQL 8.0.28 の双方向(循環)レプリケーション環境において、以下のようなテーブルが存在していると仮定します。
1 2 3 4 5 6 7 8 |
mysql> describe db.tbl; +-------+------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+------+------+-----+---------+----------------+ | id | int | NO | PRI | NULL | auto_increment | | num | int | YES | | NULL | | +-------+------+------+-----+---------+----------------+ 2 rows in set (0.00 sec) |
このとき、以下のように同じタイミングで行の追加がおこなわれると、どちらも同じ id
の 1
へ挿入を試みるため、重複キーエラーが発生してレプリケーションが停止してしまいます。
このような事態を避けるためには、auto_increment_increment および auto_increment_offset の値を変更する必要があります。
これらの値を適切に設定することで、以下のように値が重複することなくデータを挿入することが可能になります。
あるいは、binlog_format が MIXED
や STATEMENT
の場合、挿入される値ではなく、実行されたクエリがそのまま伝播されるため、エラーは発生しません。ただし、 STATEMENT
の場合は安全ではないクエリを実行するとデータ不整合が発生する恐れがあるため、レプリケーション環境での使用は推奨されていません。
同じレコードを更新した際のデータ不整合(UPDATE や DELETE)
また、テーブルへの挿入だけでなく、同じタイミングで同じレコードを更新しようとした際にも、以下のようにデータ不整合が発生します。
なおこちらのケースでは、binlog_format の値に関わらず発生するため、アプリケーション側の処理で対応をおこなわない限り、防ぐことはできません。
まとめ
ここまで、双方向(循環)レプリケーションにおいてデータ不整合やレプリケーションエラーが発生するリスクについて、運用や設定で対処できるものや、MySQL 側では防ぐことができないものについて説明してきました。
以前の記事 でも紹介していますが、もし双方向(循環)レプリケーションのように Active/Standby や Active/Active にしたいのであれば、MySQL NDB Cluster あるいは、MySQL InnoDB Cluster を利用することで実現することができます。
特に、MySQL InnoDB Cluster については手軽に利用することが可能であり、弊社でも様々な記事を公開しております。構築手順などについても以下の記事で公開しておりますので、興味のある方は是非ご一読いただければと思います。