MySQL レプリケーション

MySQL には組み込みのレプリケーション機能 (8.0 日本語版) があります。これはソース (source) と呼ばれる MySQL サーバーから、1 台以上のレプリカ (replica) へデータをコピーする仕組みです。デフォルトではレプリケーションは非同期であり、レプリカはソースへの接続を常時維持しておく必要はありません。すべてのデータベース・特定のデータベース、あるいは特定テーブルのみのレプリケーションなどを、設定で制御できます。

MySQL のレプリケーションの利点には以下のようなものがあります。

  • スケールアウト構成: 複数のレプリカ間で負荷を分散し、パフォーマンスを向上させます。この運用環境では、すべての書き込み (挿入・更新) クエリはソースサーバーで行われる必要があります。しかし、読み取り (参照) クエリは 1 つまたは複数のレプリカで行えます。この構成によりソースが書き込み専用となるため、書き込みのパフォーマンスが向上し、またレプリカを増やした数に応じ、読み取りパフォーマンスも劇的に向上します。
  • データの保持: レプリカはレプリケーションプロセスを一時停止し後に再開できるため、対応するソースのデータを破損させることなく、レプリカでバックアップ処理を実行できます。
  • 分析 : リアルタイムのライブデータはソースで処理し、統計分析処理はレプリカで行うことで、ソースのパフォーマンスに影響を与えることなく処理を実行できます。
  • 長距離データ連携: レプリケーションを使用して、リモートサイトがデータのローカルコピーを、ソースへの永続的なアクセスなしで作成できます。

 詳細については、次を参照してください: 8.4英語版ドキュメント (8.0 日本語版ドキュメント)

レプリケーション – バイナリログ

MySQL のインスタンスでデータを変更する可能性のあるステートメント (INSERT、UPDATE、DELETE) が発生し、かつバイナリログ (8.0 日本語版)が有効になっている場合、これらのトランザクション/ステートメントはバイナリログに書き込まれます。(「データを変更する可能性がある」とは、結果的に行を更新または削除しない SQL ステートメントでも、これらはバイナリログに書き込まれるためです)。つまり、MySQL バイナリログにはサーバー上のすべてのデータ変更イベントが含まれています。MySQL 5.6 より前のバージョンでは、レプリケーションは MySQL バイナリログと各バイナリログ内のトランザクションの固有の位置を用いて管理されていました。MySQL 5.6 からは、代わりにグローバルで一意にトランザクションを特定できる、グローバルトランザクション識別子 (GTID) を使用するオプションがあります。バイナリログはレプリケーションの基盤です。

バイナリログにデータを書き込む際、3 種のフォーマット (8.0 日本語版) があります。すなわち、ステートメントベース、行ベース、または混合 (MySQL がトランザクションをバイナリログに書き込む際に、より効率的な方法を決定して選択します) の 3 種です。

レプリケーションを有効にする前に、ソースサーバーからレプリカサーバーにデータをコピーして、両方のサーバーが同じデータから始まるように同期する必要があります。その後、レプリケーションをオンにすると、レプリカはソースサーバーに接続し、レプリカが最後にソースサーバーに接続してから (または初期にダンプとロードで同期したデータから) 発生したすべてのデータベース変更を要求します。これらのデータベース変更はバイナリログに保存されています。バイナリログフォーマットとレプリケーション中に使用される用語の関係は次の通りです。

  • ステートメントベースのバイナリログを用いる場合、ソースは SQL ステートメントをバイナリログに書き込みます。ソースからレプリカへのレプリケーションは、レプリカで SQL ステートメントを実行することによって機能します。これはステートメントベースレプリケーション (SBRとも略されます) と呼ばれ、MySQL のステートメントベースバイナリログフォーマットに対応しています。
  • 行ベースのログを用いる場合、ソースは個々のテーブル行がどのように変更されたかを示すイベントをバイナリログに書き込みます。ソースからレプリカへのレプリケーションは、テーブル行の変更を表すイベントをレプリカにコピーすることによって機能します。これは行ベースレプリケーション (RBRとも略されます) と呼ばれます。行ベースのログは MySQL のデフォルトのレプリケーションであり、一部のレプリケーショントポロジーでは、行ベースが唯一のオプションとなります。
  • 発生した変更ごとに最も適した形式を用いるよう、ステートメントベースと行ベースのログの両方を混合して使用するように MySQL を構成することもできます。これは混合フォーマットログ (8.0 日本語版) と呼ばれます。混合フォーマットログを使用する場合、デフォルトではステートメントベースのログが使用されます。特定のステートメントや用いられているストレージエンジンに応じて、特定のケースでは自動的に行ベースに切り替わります。混合フォーマットを用いたレプリケーションは、混合ベースレプリケーションまたは混合フォーマットレプリケーションと呼ばれます。

MySQL サーバーはこれらのデータ変更トランザクションを連番のバイナリログファイル (mysql-bin.000001、mysql-bin.000002 など) に書き込みます。各トランザクションにはバイナリログ先頭からのバイト位置が与えられます。例えば、あるトランザクションは mysql-bin.000004 という名前のバイナリログに書き込まれ、バイト位置 576 占めるとしましょう。次のトランザクションは同じログファイルに追加され (ログファイルがいっぱいになって新しいバイナリログが作成されていない限り)、そして新しいバイト位置、例えば 947、が割り当てられます。レプリカサーバーは I/O スレッドを介してソースサーバーに接続し、「特定のバイナリログの後と、そのログ内の特定位置の後のすべてのトランザクションを教えてください」と要求します。レプリカはそれらの得られたトランザクションを、レプリカのリレーログにコピーします。リレーログは一時的なログ (バイナリログと同様) です。別のスレッド、SQL スレッドが、リレーログからの変更をレプリカのデータベースに適用します。レプリカでバイナリログを有効にして、別のレプリカがそれをソースとして使用することもできますが、この記事ではそれについては触れません。

mysqlbinlog (8.0 日本語版) ユーティリティを用いてバイナリログの中身を見ることができます。「test」という名前のデータベースを作成し、バイナリログを確認すると次のようになります (出力は、そのトランザクションのみを表示するように切り詰められています)。

# mysqlbinlog binlog.000086
...
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
/*!80011 SET @@session.default_collation_for_utf8mb4=255*//*!*/;
/*!80016 SET @@session.default_table_encryption=0*//*!*/;
create database test
/*!*/;
...

また、次のようなコマンドでバイナリログの位置を確認できます (end_log_pos の値はトランザクションごとに異なります)。

#250224 16:40:14 server id 1  end_log_pos 345 CRC32 0x7c1243c6 Query thread_id=9 exec_time=0 error_code=0 Xid = 4

各トランザクションにはバイナリログ内で独自の位置があるため、レプリカはソースと同期を維持するために必要なトランザクションをソースに伝えることができます。

グローバルトランザクション識別子 (GTID)

GTID は MySQL 5.6 で導入され、レプリケーション管理を簡素化し、より堅牢にします。GTID は、MySQL サーバーでコミットされたすべてのトランザクションに割り当てられる一意な識別子です。GTID は 2 つの部分から構成されています。サーバーの一意な識別子であるサーバー UUID と、その後にトランザクションシーケンス番号が続きます。例えば、7d73b822-75e1-11ef-a4da-4455e16762b4:553 と表される GTID は、そのサーバー固有の UUID である 7d73b822-75e1-11ef-a4da-4455e16762b4 と、そのサーバーにおける一意なトランザクション番号である 553 で構成されます。次の GTID は 7d73b822-75e1-11ef-a4da-4455e16762b4:554 となり、最後の GTID の接尾辞 553 を 1 つ増やすだけです。

サーバーの UUID は、以下のコマンドで確認できます。

mysql> show variables like 'server_uuid';
+---------------+--------------------------------------+
| Variable_name | Value                                |
+---------------+--------------------------------------+
| server_uuid   | 7d73b822-75e1-11ef-a4da-4455e16762b4 |
+---------------+--------------------------------------+
1 row in set (0.01 sec)

サーバーは各々独自の UUID を持っているため、GTID のフォーマットはレプリケーショントポロジー全体で発生するあらゆるトランザクションがグローバルに一意であることを保証し、GTID がどのサーバーから発信されたかは関係ありません。

バイナリログ位置よりも GTID を用いる利点

GTID の使用には、いくつかの利点があります。

  • フェイルオーバーの簡素化: ソースまたはプライマリサーバーに障害が発生した場合、レプリカを新しいプライマリに昇格させるのが容易になります。また複数のレプリカがある場合、元のプライマリサーバーからのもっとも新しいデータセットがどのレプリカに含まれているかが確認でき、そのレプリカを新しいプライマリに昇格させることができます。
  • サーバーの一貫性: すべてのトランザクションに一意な UUID があるため、レプリカがこれらのトランザクションを正しい順序で適用し、重複トランザクションを回避することが容易になります。
  • マルチプライマリサポート: MySQL のグループ・レプリケーション (8.0 日本語版) では複数の書き込みサーバーが許可されていますが、GTID は各トランザクションがソースサーバーに一意にトレースバックできるため、競合の防止に役立ちます。
  • トラブルシューティングが容易: どのトランザクションがソースサーバーから取得済みで、どのトランザクションがレプリカサーバーに適用済みかを追跡できるため、レプリケーションで生じる問題のデバッグが容易になります。トランザクションのスキップも容易で、バイナリログ内で次の位置を特定する必要はありません。なぜなら、失敗したトランザクションの GTID より 1 つ大きい値に次の GTID を設定するだけで、単一 (場合によっては複数) のトランザクションをスキップできます。mysqlbinlog (8.0 日本語版) ユーティリティを用いると、単一 (または複数) の GTID をバイナリログ内で検索できます。

サーバー のUUID を変更したい場合 (それによってGTIDも変更されます)、まず次のコマンドで新しいUUIDを作成してください。

mysql> select UUID();
+--------------------------------------+
| UUID()                               |
+--------------------------------------+
| b7e2cda6-f30c-11ef-a736-5178638a8911 |
+--------------------------------------+
1 row in set (0.00 sec)

その後、MySQL データディレクトリ内の auto.cnf ファイルに保存されている UUID を更新/置換します。新しい UUID をアクティブにするためには、 MySQL インスタンスを再起動する必要があります (注意: MySQL HeatWaveインスタンスではUUIDを変更できません)。

GTID を用いたレプリケーションのトラブルシューティングの基本

レプリケーションの設定 (8.0 日本語版) 方法については説明しませんが、GTID を使用してトラブルシューティングを行う方法や、レプリカサーバーの現在のステータスを確認する方法を説明します。

レプリカから「SHOW REPLICA STATUS」コマンドを発行すると、出力の最後の方に次のようなものが表示されるはずです。

Retrieved_Gtid_Set: 2bdf2d84-f30e-11ef-a736-5178638a8911:1-25000
Executed_Gtid_Set: 2bdf2d84-f30e-11ef-a736-5178638a8911:1-24600

最初の行の Retrieved_Gtid_Set は、ソースの MySQL インスタンスから取得された GTID を示しています。この例では、25000 番目のトランザクションを取得しています。次の行、Executed_Gtid_Set は、レプリカインスタンスで実行されたトランザクションの数を示しています。この例では、レプリカは 24600 番目までのトランザクションを適用しており、レプリカはソースデータベースより 400 トランザクション遅れていることを意味します。書き込みアクティブなソースインスタンスがある場合、レプリカでこのコマンドを発行するたびに、取得済みの GTID の数は増加します – そしてレプリケーションがエラーなく実行されている場合、実行済みの GTID の数も増加するはずです。

注意すべき点の 1 つに、レプリカデータベースで何らかのトランザクションが実行された場合、それらのトランザクションも Executed_Gtid_Set に示されるため、実行結果は次のようになる可能性があります。

Retrieved_Gtid_Set: 2bdf2d84-f30e-11ef-a736-5178638a8911:1-25000
Executed_Gtid_Set: cb591b9a-f30e-11ef-a736-5178638a8911:1-12, 2bdf2d84-f30e-11ef-a736-5178638a8911:1-24600

Retrieved_Gtid_Set にはソースデータベースからの GTID のみが含まれますが、Executed_Gtid_Set にはソースとレプリカの両方のインスタンスからの GTID が含まれることは、覚えておくべき重要なポイントです。

上記の場合、レプリカの UUID は cb591b9a-f30e-11ef-a736-5178638a8911 なので、cb591b9a-f30e-11ef-a736-5178638a8911:1-12 の GTID セットは、12 個のトランザクションがレプリカデータベースで適用されたことを示しています。これらの GTID は、そのトランザクションがすでにレプリカに適用された後にソースデータベースからデータをコピーした場合、レプリケーションを破壊することはありません。よくあるレプリケーションエラーの 1 つは、ユーザーがレプリカで作成され、その後ソースで作成された場合です。そのソースの「CREATE USER」トランザクションがレプリカに適用されようとすると、レプリカにはすでにそのユーザーが MySQL インスタンスに存在するため、レプリケーションが中断します。

またソースインスタンス上で SHOW PRIMARY STATUS コマンドを発行して、ソースの GTID ステータスを確認することもできます。

*************************** 1. row ***************************
             File: mysql-bin.000004
         Position: 943210
     Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set: 2bdf2d84-f30e-11ef-a736-5178638a8911:1-26125

ソースの Executed_Gtid_Set2bdf2d84-f30e-11ef-a736-5178638a8911:1-26125 であり、26125 個のトランザクションがソースインスタンスに適用されたことを示しています。

レプリカは 25000 番までの GTID しか取得していないので、レプリカはソースデータベースより 1125 トランザクション分遅れています。

最後に、グループ・レプリケーション (8.0 日本語版) などのマルチプライマリレプリケーショントポロジーを用いている場合、各インスタンスでは SHOW REPLICA STATUS の出力に複数の異なる GTID セットがある場合があります。

Retrieved_Gtid_Set: 2bdf2d84-f30e-11ef-a736-5178638a8911:1-25000, 7a509bbe-f30f-11ef-a736-5178638a8911:1-14928
Executed_Gtid_Set: cb591b9a-f30e-11ef-a736-5178638a8911:1-12, 2bdf2d84-f30e-11ef-a736-5178638a8911:1-24600, 
7a509bbe-f30f-11ef-a736-5178638a8911:1-13858

GTID とレプリケーションを使用する際には、レプリケーショントポロジー内のどの GTID (あるいは UUID) がどのサーバーに属しているかを理解することが大切です。GTID を使用した MySQL レプリケーション (8.0 日本語版) は、セットアップと使用が比較的簡単です。ソースとレプリカのインスタンスが CPU、RAM、ストレージスペースに関して同じサイズであることを確認してください。レプリカにはより小さなシェイプを使うことも可能ですが、アプリケーションがレプリカにフェイルオーバーする必要がある場合、ソースインスタンスと同じパワーと容量が必要です。レプリケーションが稼働した後にレプリカサーバーでデータベースを更新しようとしなければ、レプリケーションは非常にスムーズに動作します。

(訳者註: 本記事の元記事は2025年2月25日に投稿されました)