はじめに
2026年4月21日に MySQL 9.7.0 がリリースされました。
MySQL 9.7.0 ではさまざまな機能追加・変更が行われており、これまで MySQL Enterprise Edition でのみ利用可能だった一部のコンポーネントが、MySQL Community Edition でも利用可能になりました。
その1つが Group Replication Primary Election Component(以降、Primary Election Component)です。
このコンポーネントを利用すると、シングルプライマリモードでフェイルオーバーが発生した際に、各 SECONDARY の更新状況を新しい PRIMARY の選出基準として利用できます。
具体的には、SECONDARY に蓄積している backlog が最も少ない、つまり 最も更新が追いついているメンバーを新しい PRIMARY として優先して選出します。
本記事では、従来の PRIMARY 選出方法で発生し得る問題を再現したうえで、Primary Election Component を利用することで、フェイルオーバー時の書き込み停止時間を短縮できるか検証します。
Group Replication の PRIMARY 選出における課題
シングルプライマリモードの Group Replication では、PRIMARY がグループから離脱すると、新しい PRIMARY が自動的に選出されます。
通常の PRIMARY 選出では、以下の順番で候補が評価されます。
- MySQL Server のバージョンが最も低いメンバー
group_replication_member_weightの値が大きいメンバーserver_uuidの辞書順が小さいメンバー
group_replication_member_weight は PRIMARY の選出優先度を指定する値で、同一条件のメンバー間では値が大きいほど優先されます。
例えば、すべて同じ MySQL バージョンで以下の構成だったとします。
| サーバー名 | ROLE | group_replication_member_weight |
|---|---|---|
| Server A | PRIMARY | 50 |
| Server B | SECONDARY | 100 |
| Server C | SECONDARY | 10 |
この状態で Server A が Group Replication から離脱すると、weight の大きい Server B が次の PRIMARY として優先されます。
しかしここで、Server B に何らかの性能低下やレプリケーション遅延が発生していた場合を考えます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Server A PRIMARY | +---- Server B | SECONDARY | weight = 100 | レプリケーション遅延あり | +---- Server C SECONDARY weight = 10 遅延なし |
この構成では、更新に追いついている Server C ではなく、weight の高い Server B が PRIMARY に選出されます。
さらに、group_replication_consistency がデフォルトの BEFORE_ON_PRIMARY_FAILOVER の場合、新しい PRIMARY に backlog が残っていると、その backlog の適用が完了するまで新しいトランザクションが待機します。
そのため、
|
1 2 3 4 5 6 7 8 |
PRIMARY 選出 ↓ 遅延している Server B が PRIMARY ↓ 未適用トランザクションを処理 ↓ 処理完了まで新しい書き込みが待機 |
となり、PRIMARY の選出自体は短時間で完了しても、アプリケーションから見たサービス復旧時間が長くなる可能性があります。
このようなケースでは、フェイルオーバー後に書き込みを再開できるまでの時間が長くなり、設定した RTO(Recovery Time Objective:目標復旧時間)を満たせなくなる可能性があります。
そこで、実際にどの程度の影響が発生するのか、また Primary Election Component を利用することでどのような効果が得られるのか検証してみます。
検証環境
今回は、MySQL 9.7.2 の3ノード Group Replication 環境を構築しました。
| サーバー名 | ROLE |
|---|---|
| Server A | PRIMARY |
| Server B | SECONDARY |
| Server C | SECONDARY |
検証では Server B の replica_parallel_workers を 1 に設定し、Server A に継続的な更新処理を実行することで、Server B にのみ意図的なレプリケーション遅延を発生させます。
今回は以下の3パターンを比較します。
| 検証 | Component | Server B weight | Server C weight |
|---|---|---|---|
| ① Component OFF(Server B を優先) | OFF | 100 | 10 |
| ② Component OFF(Server C を優先) | OFF | 10 | 100 |
| ③ Component ON | ON | 100 | 10 |
①と③では weight の条件を同一にし、Primary Election Component の ON / OFF によって PRIMARY の選出結果が変化するか確認します。
検証手順
Primary Election Component のインストール
Primary Election Component を利用した most-up-to-date 選出を有効にするには、すべてのグループメンバーにコンポーネントをインストールしたうえで、各メンバーの group_replication_elect_prefers_most_updated.enabled を ON にする必要があります。
Primary Election Component の URN は以下です。
|
1 2 |
file://component_group_replication_elect_prefers_most_updated |
各サーバーで以下を実行し、コンポーネントをインストールします。
|
1 2 |
INSTALL COMPONENT 'file://component_group_replication_elect_prefers_most_updated'; |
インストール後、各メンバーで以下を設定することで、most-up-to-date による PRIMARY 選出を有効化できます。
|
1 2 |
SET GLOBAL group_replication_elect_prefers_most_updated.enabled = ON; |
Component OFF の状態で比較する場合は、以下のように設定します。
|
1 2 |
SET GLOBAL group_replication_elect_prefers_most_updated.enabled = OFF; |
検証用テーブルの作成
PRIMARY の Server A に検証用テーブルを作成します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
CREATE DATABASE IF NOT EXISTS gr_test; CREATE TABLE IF NOT EXISTS gr_test.load_test ( id BIGINT UNSIGNED NOT NULL, counter BIGINT NOT NULL DEFAULT 0, padding VARCHAR(200) NOT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB; CREATE TABLE IF NOT EXISTS gr_test.digits ( n INT NOT NULL PRIMARY KEY ) ENGINE=InnoDB; INSERT IGNORE INTO gr_test.digits VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); |
続けて、10万件のテストデータを登録します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
INSERT IGNORE INTO gr_test.load_test (id, counter, padding) SELECT a.n + b.n * 10 + c.n * 100 + d.n * 1000 + e.n * 10000 + 1, 0, RPAD('x',200,'x') FROM gr_test.digits a CROSS JOIN gr_test.digits b CROSS JOIN gr_test.digits c CROSS JOIN gr_test.digits d CROSS JOIN gr_test.digits e; |
また、フェイルオーバー後に実際に書き込み可能になったタイミングを確認するため、確認用テーブルを作成します。
|
1 2 3 4 5 6 7 |
CREATE TABLE IF NOT EXISTS gr_test.failover_probe ( id INT PRIMARY KEY, counter BIGINT NOT NULL DEFAULT 0 ) ENGINE=InnoDB; INSERT IGNORE INTO gr_test.failover_probe VALUES (1,0); |
Server B に意図的な遅延を発生させる
Server B の replica_parallel_workers を 1 に変更し、設定を applier に反映するため Group Replication を再起動します。
|
1 2 3 4 5 |
SET GLOBAL replica_parallel_workers = 1; STOP GROUP_REPLICATION; START GROUP_REPLICATION; |
その後、Server A に継続的な更新負荷をかけます。
今回は、10万件の load_test テーブルに対して並列で UPDATE を継続実行しました。
一定時間経過後、replication_group_member_stats テーブル から、Server B に遅延が発生していることを確認します。
COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE(以降 Applier Queue)の値が、各 SECONDARY に残っている backlog の大きさを示します。
今回の検証では、おおむね以下の状態になるまで負荷を継続しました。
| サーバー | ROLE | Applier Queue |
|---|---|---|
| Server A | PRIMARY | 0 |
| Server B | SECONDARY | 約20,000 |
| Server C | SECONDARY | 0 |
Server B のみ遅延しており、Server C は更新に追いついている状態です。
フェイルオーバー時間の計測
フェイルオーバー時には、以下の2点を確認します。
- Server A の離脱から新しい PRIMARY を最初に観測するまでの時間
- Server A の離脱から確認用テーブル(
failover_probeテーブル)への書き込みが成功するまでの時間
PRIMARY の状態と Applier Queue は0.5秒間隔で監視します。
また、Server B と Server C に対して以下の UPDATE を繰り返し実行し、最初に UPDATE が成功した時刻を「書き込み再開時刻」として記録します。
|
1 2 3 4 |
UPDATE gr_test.failover_probe SET counter = counter + 1 WHERE id = 1; |
その状態で Server A を Group Replication から離脱させます。
|
1 2 |
STOP GROUP_REPLICATION; |
検証結果
ここからは各検証パターンの結果を記載します。
結果のみを確認したい場合は「5.4 検証結果の比較」を参照してください。
① Component OFF(Server B を優先)
Primary Election Component を無効化し、Server B の weight を 100、Server C の weight を 10 とした状態で検証しました。
Server A が PRIMARY の状態で、各グループメンバーの状態を確認した結果は以下のとおりです。
| 時刻 | サーバー | ROLE | STATE | Applier Queue |
|---|---|---|---|---|
| 00:26:31.853 | Server A | PRIMARY | ONLINE | 0 |
| 00:26:31.853 | Server B | SECONDARY | ONLINE | 21,280 |
| 00:26:31.853 | Server C | SECONDARY | ONLINE | 0 |
Server A の離脱処理を 00:26:31.989 に開始したところ、weight の高い Server B が PRIMARY となりました。
| 時刻 | サーバー | ROLE | STATE | Applier Queue |
|---|---|---|---|---|
| 00:26:32.375 | Server B | PRIMARY | ONLINE | 21,280 |
| 00:26:32.375 | Server C | SECONDARY | ONLINE | 0 |
Server B が PRIMARY になったことは、離脱処理開始から約0.4秒後に最初に観測できました。
しかし、Server B を PRIMARY として最初に観測した時点でも 21,280 件の Applier Queue が残っていました。
実際に Server B への書き込みが成功したのは 00:27:19.023 でした。
| 確認項目 | 結果 |
|---|---|
| Server B の Applier Queue | 21,280件 |
| PRIMARY 初回観測までの時間 | 約0.4秒 |
| Server A 離脱から書き込み成功まで | 約47秒 |
| 新 PRIMARY | Server B |
PRIMARY への切り替わり自体は短時間で確認できましたが、遅延している Server B が PRIMARY となったため、未適用トランザクションに追いつくまで書き込みが待機しました。
その結果、今回の検証条件では実際に書き込み可能になるまで約47秒を要しました。
② Component OFF(Server C を優先)
次に、Primary Election Component は無効のまま、Server B と Server C の weight のみを入れ替えて同様の検証を実施しました。
| サーバー | weight |
|---|---|
| Server B | 10 |
| Server C | 100 |
Server A が PRIMARY の状態で、各グループメンバーの状態を確認した結果は以下のとおりです。
| 時刻 | サーバー | ROLE | STATE | Applier Queue |
|---|---|---|---|---|
| 00:46:34.064 | Server A | PRIMARY | ONLINE | 0 |
| 00:46:34.064 | Server B | SECONDARY | ONLINE | 22,351 |
| 00:46:34.064 | Server C | SECONDARY | ONLINE | 0 |
Server B には 22,351 件の Applier Queue が蓄積している一方、Server C には遅延が発生していません。
Server A の離脱処理を 00:46:33.884 に開始したところ、weight の高い Server C が PRIMARY となりました。
| 時刻 | サーバー | ROLE | STATE | Applier Queue |
|---|---|---|---|---|
| 00:46:34.579 | Server B | SECONDARY | ONLINE | 22,351 |
| 00:46:34.579 | Server C | PRIMARY | ONLINE | 0 |
Server C が PRIMARY になったことは、離脱処理開始から約0.7秒後に最初に観測できました。
一方、Server C への書き込みは 00:46:34.503 に成功しており、Server A の離脱処理開始から約0.6秒で書き込みを再開できました。
| 確認項目 | 結果 |
|---|---|
| Server B の Applier Queue | 22,351件 |
| PRIMARY 初回観測までの時間 | 約0.7秒 |
| Server A 離脱から書き込み成功まで | 約0.6秒 |
| 新 PRIMARY | Server C |
①では遅延している Server B が PRIMARY となったことで、書き込み可能になるまで約47秒を要しました。
一方、weight を入れ替えて遅延していない Server C を PRIMARY にした場合は、Server B に約22,000件の Applier Queue が残っている状態でも、約0.6秒で書き込みを再開できました。
③ Component ON
最後に Primary Election Component を有効化し、weight は①と同じ条件に戻して検証しました。
| サーバー | weight |
|---|---|
| Server B | 100 |
| Server C | 10 |
つまり、Component OFF であれば Server B が優先される条件ですが、Component ON では Server C が選出される想定です。
Server A が PRIMARY の状態で、各グループメンバーの状態を確認した結果は以下のとおりです。
| 時刻 | サーバー | ROLE | STATE | Applier Queue |
|---|---|---|---|---|
| 00:57:23.482 | Server A | PRIMARY | ONLINE | 0 |
| 00:57:23.482 | Server B | SECONDARY | ONLINE | 22,472 |
| 00:57:23.482 | Server C | SECONDARY | ONLINE | 0 |
Server A の離脱処理を 00:57:23.118 に開始したところ、weight の高い Server B ではなく、Applier Queue が 0 の Server C が PRIMARY となりました。
| 時刻 | サーバー | ROLE | STATE | Applier Queue |
|---|---|---|---|---|
| 00:57:23.998 | Server B | SECONDARY | ONLINE | 22,472 |
| 00:57:23.998 | Server C | PRIMARY | ONLINE | 0 |
Server C が PRIMARY になったことは、離脱処理開始から約0.9秒後に最初に観測できました。
Server C への書き込みも 00:57:24.098 に成功しています。
| 確認項目 | 結果 |
|---|---|
| Server B の Applier Queue | 22,472件 |
| PRIMARY 初回観測までの時間 | 約0.9秒 |
| Server A 離脱から書き込み成功まで | 約1.0秒 |
| 新 PRIMARY | Server C |
Primary Election Component が有効な場合、weight では Server B が優先される条件であっても、より更新が追いついている Server C が PRIMARY となりました。
この結果から、今回の条件では Primary Election Component によって、フェイルオーバー時点でより更新が追いついているメンバーが優先されたことを確認できました。
検証結果の比較
3パターンの結果をまとめると以下のとおりです。
| 検証条件 | Server B Queue | Server B weight | Server C weight | 新 PRIMARY | 書き込み可能まで |
|---|---|---|---|---|---|
| ① Component OFF(Server B を優先) | 21,280 | 100 | 10 | Server B | 約47秒 |
| ② Component OFF(Server C を優先) | 22,351 | 10 | 100 | Server C | 約0.6秒 |
| ③ Component ON | 22,472 | 100 | 10 | Server C | 約1.0秒 |
特に注目したいのは、1行目と3行目です。
どちらも Server B の weight は 100、Server C は 10 であり、Server B に約2万件の Applier Queue が蓄積しているという、ほぼ同じ条件です。
Component が OFF の場合は weight の高い Server B が PRIMARY となり、書き込み再開まで約47秒かかりました。
一方、Component を ON にすると、weight が低くても更新が追いついている Server C が PRIMARY となり、今回の検証条件では約1秒で書き込みを再開できました。
考察
今回の検証では、Primary Election Component を無効化した場合でも、PRIMARY への切り替わりは Server A の離脱処理開始から約0.4秒後に確認できました。
しかし、遅延している Server B が PRIMARY となったことで、PRIMARY に切り替わった後も未適用トランザクションの処理が必要となり、実際に書き込み可能になるまで約47秒かかりました。
このことから、Group Replication のフェイルオーバーを評価する場合、単に MEMBER_ROLE が PRIMARY に切り替わるまでの時間だけを見るのではなく、実際に新しい PRIMARY で書き込み処理が完了するまでの時間を確認することが重要だと分かります。
Component OFF の場合でも、weight を適切に設定すれば、遅延していない Server C を優先させることは可能です。
しかし、実際の運用環境では、どの SECONDARY に遅延が発生するかを事前に判断できるとは限りません。
Primary Election Component を利用すると、フェイルオーバー発生時点の各 SECONDARY の更新状況を基準に、より更新が追いついているメンバーを PRIMARY として優先できます。
まとめ
MySQL 9.7.0 では、これまで Enterprise Edition で提供されていた Group Replication Primary Election Component が Community Edition でも利用可能になりました。
従来の weight ベースの選出では、遅延している SECONDARY が PRIMARY に選ばれる可能性があります。
今回の検証では、Primary Election Component を有効化することで、weight よりも各 SECONDARY の更新状況が優先されることを確認しました。
また、遅延している Server B が PRIMARY となった場合は書き込み再開まで約47秒を要したのに対し、Primary Election Component を有効化して Server C が選出された場合は約1秒で書き込みを再開できました。
フェイルオーバー後の RTO を重視する環境では、PRIMARY の切り替わりだけではなく、各 SECONDARY の更新状況や実際に書き込み可能になるまでの時間も考慮することが重要になります。
MySQL 9.7 で Community Edition でも利用できるようになった Primary Election Component は、そのような環境で有効な選択肢の1つになると考えられます。


