はじめに
MySQLのGroup Replication(以下、GR)は、複数のMySQL Server間でデータを複製し、障害時にはPrimaryを自動的に切り替える高可用性機能です。
GRを基盤とするInnoDB Clusterでは、MySQL Routerと組み合わせることで、Primary切り替え時のクライアントの接続先を自動的に変更できます。
一方、MySQL 8.4.0~8.4.10および9.7.0~9.7.1では、Group Replication Pluginに関する脆弱性(CVE-2026-60163)が報告されており、アップグレードを検討する理由の一つとなっています。
InnoDB Clusterでは、クラスタ全体を停止せずに各ノードを順番にアップグレードできますが、Primaryの切り替え時には一時的な接続断や書き込み中断が発生する可能性があります。
そこで本記事では、MySQL 8.4.10から9.7.2へのアップグレードを実施し、利用者側への影響について調査していきます。
検証環境
今回の検証では、MySQL 8.4.10の3ノードGR環境をInnoDB Clusterとして構成し、MySQL Router経由でPrimaryへ継続的に書き込みを行いながら、各ノードを順番にMySQL 9.7.2へアップグレードします。
なお、MySQL RouterおよびMySQL Shellについては、最新バージョンの使用が推奨されているため、執筆時で最新だった26.7.0、26.7.1をそれぞれ使用しています。
| 項目 | バージョン・構成 |
|---|---|
| MySQL Server(更新前) | 8.4.10 |
| MySQL Server(更新後) | 9.7.2 |
| MySQL Router | 26.7.0 |
| MySQL Shell | 26.7.1 |
| GR構成 | 3ノード、Single-Primary |

アップグレードの基本的な流れ
InnoDB Clusterをアップグレードするには、各ノードを1台ずつ停止し、アップグレード後にクラスタへ再参加させます。
今回はInnoDB Clusterの公式アップグレード手順に沿って、次の順序で更新します。
- Secondaryを1台ずつ停止・アップグレードし、クラスタへ再参加させる
- すべてのSecondaryを更新した後、Primaryを停止し、新Primaryの自動選出を確認する
- 旧Primaryをアップグレードし、Secondaryとして再参加させる
- 必要に応じてPrimaryを元のノードへ戻す
ここで、Primaryを停止する前に、別のノードへ切り替えなくてもよいのかと疑問に思うかもしれません。
注意したいのが、バージョンが混在している状態でのPrimary切り替えです。
GRでは、古いバージョンのノードがグループ内に残っている間、新しいバージョンのノードはSecondaryとして動作します。
また、明示的にPrimaryを変更する場合は、グループ内で最も低いMySQLバージョンのメンバーしか指定できないという制約があります。
そのため、今回のように8.4.10と9.7.2が混在している状態で、8.4.10のPrimaryから9.7.2のSecondaryへ明示的に切り替えることはできません。
一方、Primaryがグループから離脱した場合は、残ったノードの中から新しいPrimaryが自動的に選出されます。
ただし、Primary切り替え時には既存接続が切断されたり、一時的に書き込みができなくなったりする可能性があります。
そのため、本番環境でアップグレードを行う場合は、書き込みの一時停止やアプリケーション側のリトライ処理を考慮する必要があります。
以上を踏まえ、ここからは実際に書き込み処理を継続しながら各ノードをアップグレードし、その影響を確認していきます。
検証方法
1. 事前確認
まず、GRのメンバー状態と各ノードのバージョンを確認します。
|
1 2 3 4 |
SELECT MEMBER_HOST, MEMBER_STATE, MEMBER_ROLE, MEMBER_VERSION FROM performance_schema.replication_group_members ORDER BY MEMBER_HOST; |
|
1 2 3 4 5 6 7 8 |
+-------------+--------------+-------------+----------------+ | MEMBER_HOST | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | +-------------+--------------+-------------+----------------+ | node1 | ONLINE | PRIMARY | 8.4.10 | | node2 | ONLINE | SECONDARY | 8.4.10 | | node3 | ONLINE | SECONDARY | 8.4.10 | +-------------+--------------+-------------+----------------+ |
2. テストデータの準備と書き込み負荷の開始
アップグレード中の書き込み処理への影響を確認するため、MySQL Router経由で継続的にINSERTを実行します。
まず、連続INSERTの結果を照合できるよう、INSERTごとに一意なIDを持つテーブルを用意します。
|
1 2 3 4 5 6 7 |
CREATE TABLE test.write_log ( request_id CHAR(36) NOT NULL, created_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), payload VARCHAR(100) NOT NULL, PRIMARY KEY (request_id) ); |
今回は、MySQL Router(ポート6446)経由で接続し、INSERT成功後に約0.5秒の間隔を空けて実行するスクリプトを用意しています。
スクリプトでは、MySQL Shellのセッションを維持した状態で継続的にINSERTを実行します。
接続先には、以下のようにMySQL Routerを指定します。
|
1 2 |
mysqlsh --sqlc -h 127.0.0.1 -P 6446 -u verify_writer -p |
また、スクリプト実行中に接続エラーが発生した場合は、待機時間を設けずに再接続とリトライを試みます。
その際、何回目の試行で処理が成功したか、成功するまでに何秒かかったかも記録します。
3. SecondaryをMySQL 9.7.2へアップグレードする
まず、Secondaryであるnode2をMySQL 9.7.2へアップグレードします。
node2でMySQL Serverを停止し、既存クラスタから離脱させます。
|
1 2 |
systemctl stop mysqld |
実行前後の時刻は以下のとおりで、停止コマンドは約6秒で完了しました。
|
1 2 3 |
05:23:53 AM 05:23:59 AM |
この状態でGRのメンバー状態を確認すると、node2が離脱していることが分かります。
|
1 2 3 4 5 6 7 |
+-------------+--------------+-------------+----------------+ | MEMBER_HOST | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | +-------------+--------------+-------------+----------------+ | node1 | ONLINE | PRIMARY | 8.4.10 | | node3 | ONLINE | SECONDARY | 8.4.10 | +-------------+--------------+-------------+----------------+ |
この間もnode1がPrimaryとして動作しているため、書き込み処理が継続できるか確認します。以下は、スクリプトのログの一部です。
|
1 2 3 4 5 |
05:23:59.833Z SUCCESS seq=120 attempt=1 elapsed=28ms 05:24:00.375Z SUCCESS seq=121 attempt=1 elapsed=28ms 05:24:00.916Z SUCCESS seq=122 attempt=1 elapsed=28ms 05:24:01.458Z SUCCESS seq=123 attempt=1 elapsed=28ms |
node2の停止後も、掲載したログでは約0.5秒間隔で書き込みが成功していることを確認できました。
その後、node2をMySQL 9.7.2へアップグレードし、MySQL Serverを起動します。
なお、本記事では書き込み処理への影響に焦点を当てているため、MySQL Server自体のアップグレード手順は省略します。
実際の作業では、事前チェックやバックアップの取得、MySQL Shell/Routerおよびクラスタメタデータの更新について、公式ドキュメントをご確認ください。
また、GRの公式アップグレード手順では、アップグレード途中の意図しない再参加を防ぐため、group_replication_start_on_boot=OFFにすることが案内されています。
GRへの再参加方法についても、公式手順をご確認ください。
アップグレード後にGRの状態を確認すると、node2がSecondaryとして再参加し、バージョンが9.7.2になっていることが分かります。
|
1 2 3 4 5 6 7 8 |
+-------------+--------------+-------------+----------------+ | MEMBER_HOST | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | +-------------+--------------+-------------+----------------+ | node1 | ONLINE | PRIMARY | 8.4.10 | | node2 | ONLINE | SECONDARY | 9.7.2 | | node3 | ONLINE | SECONDARY | 8.4.10 | +-------------+--------------+-------------+----------------+ |
続いて、node3についても同様の手順でアップグレードします。
実行手順や書き込み処理の継続性、メンバーの状態確認はnode2のときと同様ですので、詳細は割愛します。
node3のアップグレード完了およびGR再参加後の状態は以下のとおりです。
これでSecondaryのアップグレードが完了しました。
|
1 2 3 4 5 6 7 8 |
+-------------+--------------+-------------+----------------+ | MEMBER_HOST | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | +-------------+--------------+-------------+----------------+ | node1 | ONLINE | PRIMARY | 8.4.10 | | node2 | ONLINE | SECONDARY | 9.7.2 | | node3 | ONLINE | SECONDARY | 9.7.2 | +-------------+--------------+-------------+----------------+ |
4. PrimaryをMySQL 9.7.2へアップグレードする
Secondaryの更新が完了したため、最後にPrimaryであるnode1をアップグレードします。
補足として、ここで8.4.10と9.7.2が混在している状態で、明示的にPrimaryを切り替えられるか確認します。
MySQL Shellから以下を実行し、node2をPrimaryへ切り替えてみます。
|
1 2 |
cluster.setPrimaryInstance('node2:3306'); |
|
1 2 3 4 5 |
Setting instance 'node2:3306' as the primary instance of cluster 'grCluster'... Failed to set 'node2:3306' as primary instance: The function 'group_replication_set_as_primary' failed. Error processing configuration start message: The appointed primary member is not the lowest version in the group. Instance cannot be set as primary (RuntimeError) |
やはりGRの仕様により、バージョンが高いnode2へ明示的に切り替えることはできませんでした。
では、改めてnode1をGRから離脱させるため、MySQL Serverを停止します。
|
1 2 |
systemctl stop mysqld |
停止コマンドの実行前後の時刻は以下のとおりでした。
|
1 2 3 |
05:29:21 AM 05:30:18 AM |
node1を停止すると、現在のPrimaryがグループから離脱するため、新しいPrimaryが選出されます。
すると、実行していたスクリプトには以下の出力がありました。
|
1 2 3 4 5 6 7 8 9 10 11 |
05:29:21.247Z SUCCESS seq=25 attempt=1 elapsed=27ms 05:29:21.759Z REQUEST seq=26 attempt=0 elapsed=0ms 05:29:24.659Z INSERT_ERROR seq=26 attempt=1 elapsed=2900ms ERROR: 2013 (HY000): Lost connection to MySQL server during query 05:29:24.669Z RESULT_UNKNOWN seq=26 attempt=1 elapsed=2910ms 05:29:24.687Z RECONNECT_ATTEMPT seq=26 attempt=1 elapsed=2928ms Connecting to Router 127.0.0.1:6446 05:29:25.234Z RECONNECTED seq=26 attempt=1 elapsed=3474ms 05:29:25.259Z NOT_FOUND seq=26 attempt=1 elapsed=3500ms UUID not committed; retry same INSERT 05:29:25.287Z SESSION_CHANGED seq=26 attempt=2 elapsed=3528ms Connection ID or primary host changed from node1/542 05:29:25.297Z SUCCESS seq=26 attempt=2 elapsed=3528ms 05:29:25.838Z SUCCESS seq=27 attempt=1 elapsed=28ms |
node1の離脱時にはERROR 2013が発生し、一時的に書き込みが失敗しました。
エラーが発生したのはseq=26のINSERTです。
接続断により書き込み結果が不明になったため、再接続後にseq=26に対応するUUIDでデータの有無を照合したところ、反映されていないことを確認できました(NOT_FOUND)。
そのため、同じIDでINSERTを再実行し、2回目の試行(**attempt=2**)、つまり再試行1回で成功しました。
今回のログでは、seq=26のINSERTリクエスト開始から、接続エラーの検知、再接続、UUID照合、再試行によって処理が成功するまでに約3.53秒かかりました。
また、node2からメンバーの状態を確認すると、node1が離脱し、node2がPrimaryになっていることを確認できました。
|
1 2 3 4 5 6 7 |
+-------------+--------------+-------------+----------------+ | MEMBER_HOST | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | +-------------+--------------+-------------+----------------+ | node2 | ONLINE | PRIMARY | 9.7.2 | | node3 | ONLINE | SECONDARY | 9.7.2 | +-------------+--------------+-------------+----------------+ |
ここまでの検証で、Primaryの離脱時にはスクリプト側でも一時的な書き込み中断が発生することが分かりました。
ただし、MySQL Router経由で再接続し、リトライを行うことで書き込みを再開できています。
では続けて、node1をMySQL 9.7.2へアップグレードし、メンバー状態を確認します。
すると以下のようになり、node1がSecondaryとして参加できています。
|
1 2 3 4 5 6 7 8 |
+-------------+--------------+-------------+----------------+ | MEMBER_HOST | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | +-------------+--------------+-------------+----------------+ | node1 | ONLINE | SECONDARY | 9.7.2 | | node2 | ONLINE | PRIMARY | 9.7.2 | | node3 | ONLINE | SECONDARY | 9.7.2 | +-------------+--------------+-------------+----------------+ |
ここまでで全ノードのアップグレードが完了しました。
最後に必要に応じて、MySQL Shellからnode1をPrimaryに昇格させます。
|
1 2 |
cluster.setPrimaryInstance('node1:3306'); |
最終的なメンバー状態は以下のようになり、各ノードのアップグレードが完了し、Primaryの配置も元に戻すことができました。
|
1 2 3 4 5 6 7 8 |
+-------------+--------------+-------------+----------------+ | MEMBER_HOST | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | +-------------+--------------+-------------+----------------+ | node1 | ONLINE | PRIMARY | 9.7.2 | | node2 | ONLINE | SECONDARY | 9.7.2 | | node3 | ONLINE | SECONDARY | 9.7.2 | +-------------+--------------+-------------+----------------+ |
検証結果のまとめ
今回のアップグレードについて、各工程における書き込みへの影響と、Primary切り替え時の測定結果をまとめます。
| 確認項目 | Secondary更新時 | Primary更新時 |
|---|---|---|
| 書き込み処理 | 継続して成功 | 一時的に失敗後、リトライで成功 |
| 接続エラー | 発生なし | ERROR 2013が発生 |
| INSERTのリクエスト開始から成功までの時間 | 通常時は約28ms | 約3.53秒(seq=26、再試行を含む) |
| 書き込み再開までのリトライ回数 | リトライなしで継続 | 1回 |
Secondary更新中は書き込みを継続できた一方、Primary切り替え時には一時的な書き込み失敗が発生しました。MySQL Routerを利用しても、アプリケーション側での再接続・リトライは考慮する必要があります。
また、今回のseq=26では、接続断後に同じIDの反映状況を照合してからINSERTを再送しました。
接続が切れた場合には、単純にSQLを再送するのではなく、処理がすでに反映されていないか確認することも重要です。
まとめ
今回は、MySQL 8.4.10からMySQL 9.7.2へのアップグレードを例に、GRのアップグレード時の影響について調査しました。
結果として、Secondaryの更新中は書き込みを継続できましたが、Primaryの離脱時には一時的な書き込み中断が発生しました。
そのため、実際の運用では、アップグレード時の書き込み中断を考慮し、実行タイミングやアップグレード方法を検討する必要があります。
また、今回はInnoDB ClusterとMySQL Routerを使用したため接続先の切り替えは自動で行われましたが、接続断後の再接続や失敗した処理のリトライは、アプリケーション側でも考慮することが重要です。
本記事の検証結果が、MySQL Group Replicationのアップグレードを検討する際の参考になれば幸いです。


