この記事は レコチョク Advent Calendar 2024 の4日目の記事となります。
はじめに
私は主に音楽サブスクリプションサービスのバックエンド開発を担当しています。 今回は、大規模サービスならではのAmazon Aurora MySQLのテーブル変更で直面した3つの課題についてご紹介します。
背景・環境
対象のサービスは、会員数が100万人規模の音楽サブスクリプションサービスです。 2024年10月中旬に大型アップデートに伴うAPIをリリース予定でした。 APIをリリースするにあたり、事前に新規テーブルの作成や既存テーブルのカラム修正を行う必要がありました。 これまでもAPIのリリースやテーブル変更は幾度となく行っていましたが、今回のリリース規模は前例がありませんでした。 このリリースを行うにあたり、いくつかの課題があったためご紹介します。 同じ問題を抱えている方の参考になれば嬉しいです。
稼働中の環境
- EC2
- RDS
- エンジンバージョン:5.7.mysql_aurora.2.11.3
- 数千万から数億レコードのテーブルが複数存在しています。
当初の実行方針
当初は、以下のスケジュールを想定していました。
1. サービス全体をメンテナンス状態にする 2. ユーザからのアクセスが来なくなったことを確認 3. 稼働中のDBに対して直接CREATE TABLEやALTER TABLEを実行
- CREATE TABLE:約150テーブル実施
- ALTER TABLE:約200テーブル実施
4. APIのリリース 5. 動作確認 6. メンテナンスを解除
課題1: メンテナンス時間が長すぎる
事前にメンテナンス時間をユーザに告知する必要があったため作業の見積もりを行いました。 本番同等のスペックやデータを用意し、テーブル作成やテーブルのカラム変更を行いました。 しかし、予想以上にカラム変更に時間がかかり、最終的に約8時間もの間メンテナンスを行う必要があることが判明しました。 ユーザ影響が大きすぎると判断し、スケジュールを見直すことに決めました。
課題1の解決策
作業時間を短縮できる部分を検討しましたが、頭打ちの状態でした。 メンテナンス方法を抜本的に変える必要があると判断し、サービス内での作業実績がない Blue/Green Deploymentsを検討することにしました。
検討した結果、メンテナンス時間は約3時間で済むと判断しました。 そして、新たに決定したスケジュールが以下です。
1.リリース当日までに
- Blue/Green環境構築
- Green環境のDBに対してCREATE TABLEやALTER TABLEを実行
2. リリース当日
- サービス全体をメンテナンス状態にする
- トラフィックを切り替え
- APIのリリース
- 動作確認
- メンテナンス解除
Blue/Green Deploymentsの概要
詳しい内容はたくさんの記事があるのでここでは簡単にご紹介するまでに留めます。
Blue/Green Deploymentsは、稼働中のDBに対して直接CREATEやALTERを行うのではなく あらかじめ新しい環境を用意しCREATEやALTERを行った後、トラフィックを新しい環境に切り替えます。 これによりダウンタイムを最小限に抑え、問題が発生した場合はすぐに古い環境に戻すことができます。
一般的な流れは以下の通りです。 1. Blue環境を作成 2. Green環境を作成 3. Green環境上でテスト 4. テストが問題なければトラフィックをGreen環境に切り替え 5. Blue環境を削除
課題2: Blue/Green環境作成後、環境間のデータ同期が取れていない
Blue/Green環境を無事構築し、Green環境のDBに対して新規テーブル作成とカラム変更を行っていました。 まず1テーブルのみint型をbigint型に修正するDDLを実行し、どのくらい時間がかかるか計測しました。
ALTER TABLE `テーブル名` MODIFY COLUMN `カラム名` bigint unsigned NOT NULL;
しかし、上記DDLを実行完了後30分経過してもBlue環境のデータがGreen環境に同期されていない問題が発生しました。
課題2の原因と解決策
Green環境で以下のコマンドを実行し、スレーブの状態を確認しました。 (MySQL 8.0.22 以降では、SHOW SLAVE STATUS は非推奨になり、かわりに SHOW REPLICA STATUS の使用が推奨されています)
MySQL 5.7:https://dev.mysql.com/doc/refman/5.7/en/replication-administration-status.html MySQL 8.0:https://dev.mysql.com/doc/refman/8.0/ja/replication-administration-status.html
SHOW SLAVE STATUS\G
(出力結果は一例)
SHOW SLAVE STATUS\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: xxxx
Master_User: root
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin-xxxx
Read_Master_Log_Pos: 25877182
Relay_Log_File: relaylog.xxxx
Relay_Log_Pos: 63380587
Relay_Master_Log_File: mysql-bin-changelog.xxxx
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table: xxxxx
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 63380354
Relay_Log_Space: 160096810
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: Yes
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 775
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: xxxx
Master_UUID: xxxx
Master_Info_File: mysql.xxxx
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Waiting for table metadata lock
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
多数の項目がある中で特に次の項目に注目しました。
- Slave_IO_Running / Slave_SQL_Running
- Slave_IO_Running: Yes は、IOスレッドが正常に動作していることを示す
- Slave_SQL_Running: Yes は、SQLスレッドが正常に動作していることを示す
- 通常、レプリケーションを開始していない場合や、明示的にSTOP SLAVEで停止した場合を除き、この項目は「Yes」であるべき
- Seconds_Behind_Master
- この値が「0」であれば、レプリカで処理中のイベントがない(=同期している)状態
- Last_Error / Last_IO_Error / Last_SQL_Error エラーが発生している場合、ここにエラー内容が表示される
上記の一例の場合、 Slave_IO_Running と Slave_SQL_Running が Yes であるが Seconds_Behind_Masterの値が775であることから、同期がまだ終わっていないことがわかります。 最終的にGreen環境にもBlue環境のデータが反映され、 Seconds_Behind_Masterの値が0になったのはALTER TABLEの実行が完了した約1時間後でした。 なぜ同期に1時間もかかったのか、
はぜ