AI事業本部 アドテクディビジョン SREグループに所属している平田聡一朗と申します。SREグループではAI事業本部 アドテクディビジョンをメインとしてその他事業部のインフラおよびSRE支援を担当しているグループです。
本記事ではDynalystにおけるMySQLアップデートの取り組み内容について説明します。

はじめに

Dynalystとは

Dynalystとはスマートフォン向けのパフォーマンス広告配信プラットフォームです。秒間数十万リクエスト規模のトラフィックを処理しており、この膨大なトラフィックを支えるインフラは「AWS」と「Cycloud」のハイブリッドクラウド環境で運用されています。

「AWS」から「Cycloud」への移設について

2024年に「AWS」のコスト削減を目的に、ビッドサーバーを「Cycloud」内のマネージドサービス「AKE」へ移設しました。その際「Aurora MySQL」をマスター、「Cycloud」に構築したMySQLをレプリカとするハイブリッドなデータベースアーキテクチャを採用しています。「Cycloud」移設の取り組み内容についての詳細は、こちらの資料をご確認ください。

  ハイブリッドクラウド構成の一部

「Cycloud」への移設時、「Aurora MySQL v2」(MySQL5.7互換)を使用していたため、そのバージョンに合わせて「Cycloud」にMySQLを構築しました。しかし2024年10月31日にサポート終了が発表されており(サポート延長料金は2024年12月1日から発生)、その期限までにアップデートが完了しないと延長サポート料金が発生します。

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraMySQLReleaseNotes/AuroraMySQL.release-calendars.html

Dynalystでは大規模なデータベースクラスターを運用しており、延長サポート料金は多大なコストを伴います。そのため「Aurora MySQL」および「Cycloud」に構築したMySQLのアップデートが必要です。また数時間のサービス停止はビジネス要件として許容できませんでした。したがって十分な検証を行い、慎重かつ最小限のダウンタイムでアップデートを実施することが求められました。

切り替え作業前の取り組み

差分確認

アップデートに伴い非推奨なパラメータや新しく追加された機能が多数あります。しかしすべての内容を確認するのは時間がかかるため、ここでは下記項目に絞ってリリースノートを確認し、対応が必要かどうかをチーム内で共有しました。

  • 互換性のない変更(Incompatible Change)
  • 非推奨及び削除される機能(Deprecation and Removal Notes)
  • 重要な変更(Important Change)

またMySQL Shellが提供するアップグレードチェッカーを利用し、リリースノートで確認した内容に抜け漏れがないか確認しました。「Aurora MySQL」特有の仕様によりアップグレードチェッカーに引っかかる項目もありましたが、私たちのチームでは対応不要と判断しました。

バージョンアップ方法の検討

「AWS」では以下の3点のバージョンアップ方式が提供されております。

  1. スナップショット/リストア方式
  2. インプレース方式
  3. Blue Green Deployment

上記で説明したように、サービスを停止することがビジネス要件として許容できないため、最小限のダウンタイムでバージョンアップが可能なBlue Green Deployment方式を採用することにしました。ただし、ハイブリッドクラウド運用では、通常のBlue Green Deploymentでは切り替えができないため、後ほど切り替え手順の詳細について説明します。

データベースクラスター構築の検証

Blue Green Deploymentを利用した切り替え作業を実施する前に、「Aurora MySQL v3」(MySQL8.0互換)および「Cycloud」に構築したMySQL8.0(Cycloud MySQL8.0)でレプリケーションを構築し、ビッドサーバーから「Cycloud MySQL8.0」にトラフィックを流し性能を検証する必要があります。そのために下記のようなデータベースクラスターを用意する必要があります。データベースクラスター構築の検証時は以下の3点について確認しました。

  1. Blue Green Deploymentを利用して「Aurora MySQL v2」から「Aurora MySQL v3」を作成できるかどうか
  2. 「Aurora MySQL v3」からダンプし、「Cycloud MySQL8.0」にリストアしてレプリケーションを構築できるかどうか
  3. 「Aurora MySQL v2」にデータを挿入し、「Aurora MySQL v3」を経由して「Cycloud MySQL8.0」にレプリケーションされるかどうか

性能検証時のデータベースクラスター構成図

1点目について、クラスター名やパラメータグループを設定し、数回のクリックで「Aurora MySQL v3」を構築できます(詳細は公式ドキュメントをご確認ください)。ただしバイナリログの有効化や「Aurora MySQL v3」に対応したインスタンスサイズへの変更が必要です。

2点目について、まず「Aurora MySQL v3」に対してレプリケーションの実行に必要なユーザーを作成しました。

Aurora MySQL v3> CREATE USER ‘replica’@’%’ IDENTIFIED BY <Password>;
Aurora MySQL v3> GRANT REPLICATION SLAVE ON *.* TO ‘replica’@’%’;

続いて「Aurora MySQL v3」に対して、mysqldumpコマンドを利用してダンプを実行します。

$ /usr/local/mysql/bin/mysqldump -u <User Name> -p<Password> -h <Aurora MySQL V3 Writer Endpoint> --databases <Schema Name> --single-transaction --set-gtid-purged=ON --quick  > ./dump.sql

mysqldumpコマンド実行時に利用したいくつかオプションの詳細は、以下の通りです。

  • --single-transactionmysqldumpトランザクションを開始してからダンプを実行することで、一貫性のある状態でデータをダンプするためのオプション
  • --set-gtid-purged:GTIDが有効なデータベースで、ダンプファイルにSET @@GLOBAL.gtid_purgedを追記するオプション
  • --quick:大きなテーブルをダンプする際に、メモリ消費を抑えるために使用するオプション

その後ダンプしたデータを「Cycloud MySQL8.0」にリストアし、SHOW REPLICA STATUSコマンドを実行します。Replica_IO_RunningReplica_SQL_Runningの値が共にYesであれば、レプリケーションが正常に構築されていることが確認できます。

# リストア
$ /usr/local/mysql/bin/mysql -u <User Name> -p<Password> < ./dump.sql

# レプリケーション
Cycloud MySQL> CHANGE REPLICATION SOURCE TO SOURCE_HOST = <Aurora MySQL V3 Writer Endpoint>, SOURCE_USER = 'replica', SOURCE_PASSWORD = 'Replica User Password', SOURCE_AUTO_POSITION = 1; 
Cycloud MySQL> START REPLICA; 
Cycloud MySQL> SHOW REPLICA STATUS\G
...
 Replica_IO_Running: Yes
Replica_SQL_Running: Yes

3点目について、まず「Aurora MySQL v2」にデータを挿入し、「Aurora MySQL v3」にレプリケーションされていることを確認しました。その後同じデータが「Cycloud MySQL8.0」にもレプリケーションされていることが確認しました。これによりビッドサーバーの向き先を「Cycloud MySQL8.0」に変更し、読み取り処理の性能確認を行うことができます。

負荷試験

アップデート後に読み取り処理の性能が劣化した事例を社内および他社で認識していたため、リクエストを流す前に性能を事前に検証し、必要に応じて対策を講じます。ただし負荷試験シナリオの作成や負荷試験環境の構築には時間と手間がかかるため、今回は下記の手順で負荷試験を実施し性能の劣化がないか確認しました。

  1. Datadog Database Monitoring(DBM)から実行時間と実行回数が多いクエリをピックアップ(事前にDBMを有効化しておく必要があります)
  2. DBMからピックアップしたクエリのパラメータ部分は正規化されているため、AWS Performance Insightsやスロークエリログからパラメータを取得
  3. mysqlslapコマンドを使用して、「AWS」および「Cycloud」の各バージョンに対して負荷試験を実行し、バージョン間で性能に差異がないか確認する
$ /usr/local/mysql/bin/mysqlslap --user=<User Name> --password=<Password> --host=<DB Host> --concurrency=1000 --iterations=10 --create-schema=<Schema Name> --no-drop --query=query.sql

mysqlslapコマンド実行時に使用したいくつかオプションの詳細は、以下の通りです。

  • --concurrency:シミュレートするクライアント数
  • --iterations:実行するテストの回数
  • --create-schema:テスト対象のスキーマを指定。mysqlslapがテスト実行時にスキーマを削除しないよう、併せて--no-dropオプションも指定する

負荷試験の結果、一部のクエリで性能劣化が確認されたため、インデックスの再作成が必要でした。

性能確認

「Aurora MySQL v3」と「Cycloud MySQL8.0」を構築した後、ビッドサーバーから「Cycloud MySQL8.0」にリクエストを徐々に流して性能およびビジネス影響に問題がないか確認を実施しました。

「Cycloud」の「AKE」にデプロイされているビッドサーバーは、下記のようにKubernetesのStatefulsetを複数利用して運用しているため、初めは1つのStatefulsetだけを「Cycloud MySQL8.0」に向けて、性能に問題がなければ徐々に他のStatefulsetの向き先を変更することで、擬似的なカナリアリリースのように性能確認を行いました(この段階では幸いにも性能劣化は発生しませんでした)。

擬似的なカナリアリリースによる性能確認時の構成図

切り替え作業

切り替え作業の流れ

切り替え作業の流れについてですが、Blue Green Deploymentでは「Aurora MySQL v2」のエンドポイントが自動的に「Aurora MySQL v3」に割り当てられるため、アプリケーション側でエンドポイントの変更は不要です。そのため切り替え前は「AWS」にデプロイされているアプリケーションが「Aurora MySQL v2」を参照するように設定しておきます。ただし「AKE」にデプロイされているビッドサーバーは「Cycloud MySQL8.0」を参照したままになります。

しかしこのまま切り替え作業を実施すると問題が発生します。具体的には、CHANGE REPLICATION SOURCEで設定したSOURCE_HOSTには「Aurora MySQL v3」のエンドポイントが設定されており、切り替え作業を実施すると「Aurora MySQL v3」のエンドポイントは消失してしまうため、レプリケーションが停止してしまいます。

そのままBlue Green Deploymentを実行するとレプリケーションが停止してしまう

そこでレプリケーションを一旦停止させ、切り替え完了後に「Aurora MySQL v3」に新たに割り当てられたエンドポイントを使用してレプリケーションを再構築する手順を採用しました。レプリケーションを一時的に停止させるため、作業中はレプリケーションラグが発生しますが、事前に検証し切り替え作業時間内に発生するラグであればビジネスへの影響はないと確認したため、この手順を採用しました。

Blue Green Deploymentではタイムアウト値を設定でき、この時間内に切り替えが完了しない場合、変更はすべてロールバックされます。切り替えに失敗した場合はレプリケーションを再構築し、ラグが解消されたらタイムアウト値を増やして再度切り替え作業を実施するようにしました。

一時的にレプリケーションを停止し、切り替え後に再構築する

切り戻し環境構築

事前に参照性能の検証は行いましたが、切り替え後に何かしら問題が発生し「Aurora MySQL v2」および「Cycloud MySQL5.7」に切り戻す可能性もあります。そこで万が一に備えて切り戻し環境を構築しておく必要があります。Blue Green Deploymentでは切り替わった瞬間のバイナリログ名とポジションがコンソール上に表示されるため、これを利用して「Aurora MySQL v3」から「Aurora MySQL v2」への逆レプリケーションを構築できます。これにより切り戻しが必要な場合でも最小限のダウンタイムで実現可能です。

https://aws.amazon.com/jp/blogs/database/implement-a-rollback-strategy-after-an-amazon-aurora-mysql-blue-green-deployment-switchover/

「Aurora MySQL v3」から「Aurora MySQL v2」への逆レプリケーション

「Aurora MySQL v2」および「Cycloud MySQL5.7」削除

切り替え後、性能とビジネス影響に問題がないことが確認できたため、逆レプリケーションを停止し、「Aurora MySQL v2」および「Cycloud MySQL5.7」を削除しました。これによりMySQLのアップデート作業は完了しました。

作業中に発生した問題点と解決策

Blue Green deployment切替後にレプリケーションが再構築できない

こちらは検証環境の構築中に発生した問題です。上記で説明したように、Blue Green Deploymentで切り替えが完了した後、レプリケーションを再構築する必要がありましたが、START REPLICAを実行してもレプリケーションが再構築できていないことが確認されました。

performance_schema.replication_applier_status_by_workerテーブルで原因を確認したところ、Blue Green Deploymentで切り替えを行う際、「Aurora MySQL」ではmysql.rds_replication_statusテーブルにTRUNCATEコマンドが実行されている一方で、「Cycloud MySQL」にはmysql.rds_replication_statusテーブルが存在しないにも関わらずTRUNCATEコマンドが実行され、その結果クエリエラーが発生し、レプリケーションの再構築ができていませんでした。

Aurora MySQL v3>START REPLICA;
Aurora MySQL v3> SHOW REPLICA STATUS\G
...
 Replica_IO_Running: Yes
Replica_SQL_Running: No
...
     Last_SQL_Error: Coordinator stopped because there were error(s) in the worker(s). The most recent failure being: Worker 1 failed executing transaction 'a59a150c-b8e4-3182-9911-5b7c492a9ac3:1' at master log mysql-bin-changelog.595113, end_log_pos 392. See error log and/or performance_schema.replication_applier_status_by_worker table for more details about this failure or others, if any.

Aurora MySQL v3> select * from performance_schema.replication_applier_status_by_worker\G
*************************** 1. row ***************************
      CHANNEL_NAME:
         WORKER_ID: 1
         THREAD_ID: NULL
     SERVICE_STATE: OFF
 LAST_ERROR_NUMBER: 1146
LAST_ERROR_MESSAGE: Worker 1 failed executing transaction 'a59a150c-b8e4-3182-9911-5b7c492a9ac3:1' at master log mysql-bin-changelog.595113, end_log_pos 392; Error 'Table 'mysql.rds_replication_status' doesn't exist' on query. Default database: ''. Query: 'TRUNCATE TABLE mysql.rds_replication_status'
...

「Aurora MySQL v3」には1つのクラスター内に複数のスキーマが存在しますが、ビッドサーバーは1つのスキーマのみを参照します。そのためビッドサーバーが参照しないスキーマについては、replica-ignore-dbでレプリケーションされないように設定していました。

# ビッドサーバーが参照しないスキーマをレプリケーションさせない
replicate-ignore-db = schema1
replicate-ignore-db = schema2

ただし、mysqlスキーマをフィルタリングしていなかったため、これが原因で「Aurora MySQL v3」のmysqlスキーマで実行されたコマンドが「Cycloud MySQL8.0」でも実行されてしまっていました。そこでフィルター設定を見直し、ビッドサーバーが利用しないスキーマに加えて、MySQLが内部的に利用するスキーマ(mysql, sysなど)は明示的にフィルタリングするように設定しました。

さらにこれまではスキーマ単位でフィルター設定をしていましたが、公式ドキュメントによると、binlog_format=mixedの場合でもフィルターが正常に動作することを保証するため、テーブル単位でのフィルター設定(replicate-wild-do-table, replicate-wild-ignore-table)が推奨されています。そこで、テーブル単位でフィルター設定を行うように変更しました。

# ビッドサーバーが参照するスキーマのみレプリケーション、それ以外のスキーマはフィルタリングする
replicate-wild-do-table = schema3.%
replicate-wild-ignore-table = schema1.%
replicate-wild-ignore-table = schema2.%

https://dev.mysql.com/doc/refman/8.0/ja/replication-rules-examples.html

特に混合バイナリロギング形式 (binlog_format=MIXED) を使用している場合、レプリケーションフィルタがバイナリロギング形式に関係なく一貫して動作することを保証する必要がある場合は、テーブルレベルのレプリケーションフィルタリングオプションのみを使用し、データベースレベルのレプリケーションフィルタリングオプションは使用しないでください。 また、フィルタ処理されたテーブルとフィルタ処理されたテーブルの両方を更新する複数テーブル DML ステートメントは使用しないでください。

文字コードutf8からutf8mb4への更新

MySQL8.0では文字コードutf8が非推奨となっているため、utf8mb4への更新が必要です。 ただしALTER TABLEで文字コードを更新しようとするとテーブルロックがかかり(オンラインDDLに未対応)、サービスを停止して書き込みを停止する必要があります。そこでBlue Green Deploymentで作成された「Aurora MySQL v3」に対してALTER TABLEを実行することで、サービスを停止させることなく文字コードの更新が可能になります。

型変換の許可

レプリケーション時にはカラムのデータ型が一致している必要があります。そのため「Aurora MySQL v3」で文字コードを変更しデータがレプリケーションされるとエラーが発生しレプリケーションが停止します。

https://dev.mysql.com/doc/refman/8.0/en/replication-features-differing-tables.html#replication-features-different-data-types

そのため型変換を許容するためのパラメータreplica_type_conversionsで型が大きくなる変換を許可するALL_NON_LOSSYを設定する必要があります。このパラメータを「Aurora MySQL v3」に反映させることでレプリケーションの停止を防ぐことができます。

https://dev.mysql.com/doc/refman/5.7/en/replication-features-differing-tables.html#replication-features-different-data-types

処理時間の見積もり

レコード数が多くALTER TABLEの実行に時間がかかることが予想されたため、処理時間の見積もりを行いました。見積もりを行う手順として、まずはperformance_schema.setup_instruments(パフォーマンスデータを収集する際に、どの種類のイベントを記録するかを設定するためのテーブル)における下記項目が有効化されていることを確認します(デフォルトで有効化されております)。

https://dev.mysql.com/doc/refman/8.0/ja/performance-schema-stage-tables.html

Aurora MySQL v3> SELECT * FROM performance_schema.setup_instruments WHERE NAME LIKE 'stage/innodb/alter%';
+------------------------------------------------------+---------+-------+
| NAME                                                 | ENABLED | TIMED |
+------------------------------------------------------+---------+-------+
| stage/innodb/alter table (end)                       | YES     | YES   |
| stage/innodb/alter table (flush)                     | YES     | YES   |
| stage/innodb/alter table (insert)                    | YES     | YES   |
| stage/innodb/alter table (log apply index)           | YES     | YES   |
| stage/innodb/alter table (log apply table)           | YES     | YES   |
| stage/innodb/alter table (merge sort)                | YES     | YES   |
| stage/innodb/alter table (read PK and internal sort) | YES     | YES   |
+------------------------------------------------------+---------+-------+

続いて、performance_schema.setup_consumers(パフォーマンスデータをどのように収集するかを設定するためのテーブル)における下記項目がデフォルトで無効化されているため、こちらを有効化(ENABLED列をYESに変更)させる必要があります。

Aurora MySQL v3> SELECT * FROM performance_schema.setup_consumers WHERE NAME LIKE '%stage%';
+----------------------------+---------+
| NAME                       | ENABLED |
+----------------------------+---------+
| events_stages_current      | NO      |
| events_stages_history      | NO      |
| events_stages_history_long | NO      |
+----------------------------+---------+

ALTER TABLEの実行中に下記クエリを実行することで処理時間を見積もることができます。WORK_COMPLETED(完了した作業単位の数)の値がWORK_ESTIMATED(予想される作業単位の数)に近づくほど、完了が近いことが確認できます。

Aurora MySQL v3> SELECT EVENT_NAME, WORK_COMPLETED, WORK_ESTIMATED  FROM performance_schema.events_stages_current;
+-----------------------------+----------------+----------------+
| EVENT_NAME                  | WORK_COMPLETED | WORK_ESTIMATED |
+-----------------------------+----------------+----------------+
| stage/sql/copy to tmp table |         596833 |       89745932 |
+-----------------------------+----------------+----------------+

見積もりを行った結果、現クラスターのインスタンスサイズでは最大で数ヶ月かかる可能性があったため、一時的にインスタンスサイズをスケールアップしてから文字コードを更新しました(それでも数日程度かかりました)。

大規模クラスターにおける注意点

今回のように文字コードの更新に数日かかる場合、その期間以上に「Aurora MySQL v2」側のバイナリログの保存期間を下記コマンドで延長しておく必要があります。延長しないと「Aurora MySQL v2」側のバイナリログがローテートされることでBlue Green間でデータの差分が発生し、レプリケーションが停止する可能性があります。

Aurora MySQL v2> CALL mysql.rds_set_configuration('binlog retention hours', 168);

またバイナリログの保存期間を延長するとディスク容量が増加するため、こちらにも注意が必要です。Auroraの場合DBエンジンのバージョンに応じて最大64TBまたは128TBまでサポートされており、今回はこの範囲内に収まっていたため問題ありませんでした。

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/Aurora.Overview.StorageReliability.html

※ 文字コードを変更するにあたり、下記の技術ブログが大変参考になりましたので、ご紹介させていただきます

https://ca-srg.dev/129e573152d44becb586f78155d79022

https://ca-srg.dev/de47e8a6665e4c4697977388a895ecaf

「AWS」と「Cycloud」間のレプリケーションが停止する

この問題はアップデート作業とは直接関係ありませんが、今回のような「Aurora MySQL v2」をマスター、「元のMySQL5.7」をレプリカとしている場合には発生する可能性があるため、ご紹介させていただきます。

Sequel AceというMacOS用のツール(MySQLとMariaDBを操作するアプリケーション)を利用して「Aurora MySQL v2」に対してデータを挿入したところ、「AWS」と「Cycloud」間のレプリケーションが停止する事象が発生しました。

Aurora MySQL v2> SHOW SLAVE STATUS\G
...
Last_SQL_Error: Error 'Character set '#255' is not a compiled character set and is not specified in the '/usr/share/mysql/charsets/Index.xml' file' on query. Default database: ''. Query: 'BEGIN'
...
    Last_Error: Coordinator stopped because there were error(s) in the worker(s). The most recent failure being: Worker 1 failed executing transaction 'f71840bb-172d-3628-961e-7f25f7637ce8:13216200476' at master log mysql-bin-changelog.637810, end_log_pos 51770987. See error log and/or performance_schema.replication_applier_status_by_worker table for more details about this failure or others, if any.

原因は照合順序utf8mb4_0900_ai_ci(#255)が「Cycloud MySQL 5.7」に設定されてしまったことです。この照合順序はMySQL 8.0で導入されましたが、「Aurora MySQL v2」では先行して導入されていました。またSequel Aceでutf8mb4が設定された状態で「Aurora MySQL v2」に接続するとcollation_connection(セッション中に使用される照合順序を設定するパラメータ)にutf8mb4_0900_ai_ciが設定されます。

Sequel Aceでutf8mb4が設定された状態での照合順序の結果

「Aurora MySQL v2」 のバイナリログを確認すると、確かにSETコマンドでcollation_connectionutf8mb4_0900_ai_ciに設定されていることが確認できます。そのため「Aurora MySQL v2」では問題なくクエリが実行できましたが、「Cycloud MySQL 5.7」でもutf8mb4_0900_ai_ciが設定され、結果としてレプリケーションが停止しました。

# バイナリログをダウンロード 
$ /usr/local/mysql/bin/mysqlbinlog --no-defaults --read-from-remote-server --host=<Aurora MySQL v3 Writer Endpoint> --port=3306 --user <User Name> --password --raw --verbose --result-file=/home/ubuntu/ mysql-bin-changelog.637810 

# バイナリログ確認 
$ /usr/local/mysql/bin/mysqlbinlog --no-defaults --base64-output=DECODE-ROWS -v mysql-bin-changelog.637810 | less 
...
SET @@session.character_set_client=255,@@session.collation_connection=255,@@session.collation_server=45

暫定対策としてマイグレーションツールの統一化が挙げられましたが、アップデートが完了したため今後同様の事象が発生することを回避できました。

まとめ

本記事では、「AWS」と「Cycloud」のハイブリッドクラウド運用で実施したMySQLアップデートの取り組みについて紹介しました。

アプリケーションの修正を担当していただいたDynalyst開発チームの皆様、インフラリソースを準備していただいたCIUの皆様、技術的な相談に乗っていただいた同チームの関口さん、有益な技術ブログを提供していただいたSRG所属の鬼海さんのおかげで、無事期限内にアップデートを完了することができました。

紹介したアーキテクチャは珍しい構成かもしれませんが、どなたかの作業に役立てば幸いです。

アバター画像
2023年7月中途入社のSREエンジニアです。現在はAI事業本部 アドテクディビジョンでSRE支援を担当しております。