【MySQL】メジャーバージョンアップグレードの味方: “アップグレードチェッカーユーティリティ”を理解して活用しよう

あいさつ

こんにちは!
技術本部 技術戦略Div. リードエンジニアの関根です!
2024年9月にジョインして、コツコツSRE活動を進めておりますっ
エンジニア3年目ながらSREにゴリゴリ関われているのは、アドウェイズにオープンなコミュニケーションの文化があるおかげです。SREに少しでも興味がある人は、お待ちしておりますよ!

最近、Netflix、アマプラ、U-NEXTに加えてAppleTVも契約してしまいました笑 映画やドラマの話しましょうね!
あとお酒が大好きなので、社内外問わず飲みに行きましょうね!!

この記事はなに?

この記事は、MySQLメジャーバージョンアップグレード時にアップグレードチェッカーユーティリティ(以下、アップグレードチェッカー)を理解して活用することで、効率的にアップグレードを進めるための情報を記載しています。

SREとして、EOL対応屋さん的な動きをコツコツやっている方もいらっしゃると思います。
次のEOLは数年後...ということで、将来また調べたくない&覚えておいて役立てたい内容をまとめました。

次のEOLまで一旦のんびりしたい方は、ブクマしておいてもよいかもしれません。
特定バージョンの情報を例として出すことはありますが、将来も役に立ちそうなところを意識して書いていきます。

想定読者

  • 今後、MySQLのメジャーバージョンアップグレードを控えている方
  • 過去のメジャーバージョンアップグレード時にトラブルが発生し、プロセスを見直したい方
  • 単純にMySQLの「アップグレードチェッカーユーティリティ」に興味がある方

ゴール

今後、アップグレードチェッカーを活用できるように理解できている状態。
具体的には、アップグレードチェッカーで解決する非互換性とそうでないものを切り分けて、効率的にメジャーバージョンアップグレードを進める知識がある状態。

筆者がアップグレードチェッカーを知った背景

弊社では基本的にAWS環境でAurora・RDSを利用しており、MySQL 8系への移行も完了しています。 ただし、例外的にMySQL5.7の延長サポートを適用している環境が存在し、RDS MySQL5.7->RDS MySQL8系へのアップグレード実施が必要でした。

そのプロジェクトに関わって調査・検証するなかで、MySQLのアップグレードチェッカーを知り、以下のような理由から記事にしてまとめておきたいと考えました。

  • 8系に限らず、将来のメジャーバージョンアップグレード時にも使える知識である
  • AWS上では自動でチェックが実行されるため、理解せずになんとなく通り過ぎていることが多い印象がある
  • 理解して活用することで、アップグレードチェッカーで解決する非互換性とそうでないものを切り分け、無駄な重複チェックなどの工数を削減できる

アップグレードチェッカーの概要

MySQL公式Docによると、アップグレードチェッカーユーティリティとは、MySQL Shellからutil.checkForServerUpgrade()関数によって実行できるMySQL サーバーインスタンスのアップグレード準備ができているかどうかを確認できる機能です。

たとえば、私が検証した8系へのアップグレードでは下記のような項目をチェックしています。

  • 新しい予約語と衝突する名前のDBオブジェクトの使用
  • 64文字を超える外部キー制約名の存在
  • 削除されたシステム変数の使用 etc...


弊社では基本的にAWS環境でAurora・RDSを利用していることが多いため、その観点で補足します。
AWS公式Docによると、アップグレードをスタートすると、RDSでは非互換性を検出するために自動的に事前チェックが実行されスキップすることはできません。
いま引用したドキュメントはRDSのものですが、Auroraでも基本的に同じだと思われます(参考: Aurora Doc)。
筆者が実際に手を動かして検証したのはRDSのみですので、この記事はRDSの場合を中心に記載していきます。

自動実行であるため、AWSでAurora・RDSを利用している場合は特に意識することなくMySQLのアップグレードチェッカーを実行していることになります。(AWSではチェック項目に一部差分がありますが、後述します)
この記事のタイトルで「活用しよう」ではなく「理解して活用しよう」と名付けているのは、そこが理由です。
理解して活用しないと、アップグレードチェッカーですでに確認している項目を気づかずに重複してチェックしてしまうこともあります。。
(実際に社内外の事例を調査して、そのような例が散見されました)

アップグレードチェッカーの位置付け

アップグレードチェッカーをどのように活用すべきか理解するために、アップグレードそのものの目的から逆算して整理していきます。
また、話をシンプルにするために、ここではEOL対応としてのアップグレードを想定します。

メジャーバージョンアップグレードの目的(達成したいこと)

適切な調査・検証により、下記を担保したアップグレードを完了すること。

  • ターゲットバージョンへのDB自体のアップグレードがエラーなく成功すること
  • アップグレード前と同じように問題なくアプリケーションとDBが動作すること
  • アップグレード前と同等以上の性能でDBが運用できること

目的実現のために必要な作業

大項目として、主たるものを記載します。

  1. ターゲットバージョンの非互換性への事前対応(詳細は後述)
  2. アプリケーションとDBの動作確認(ex. ローカル、テスト環境でテストコードが通ることの確認、E2Eテストなど)
  3. アプリケーションとDBの性能試験(ex. 実行計画の比較により重大な性能差がないことの確認など)

アップグレードチェッカーの活用例

アップグレードチェッカーは、前述の1に関連するものです。 実際に私が行なった調査・検証をベースに、どのようなフローで活用できるのか、例を提示します。

  1. ターゲットバージョンの非互換性をリスト化
  2. 本番同等のデータセットを持つ複製DBをもとに、アップグレード自体が成功する状態を作る(※ここでアップグレードチェッカーを活用)
  3. 1の非互換性リストと2でチェックされた項目を突き合わせて、アップグレードチェッカーにより確認可能な非互換性とそうでないものを切り分ける
  4. それぞれ対応方法を調査する
  5. 4をもとに対応実施した上で、動作確認や性能試験に進む


補足します。

1でいう非互換性リストのソースは、「MySQL ターゲットバージョン名 非互換性」と検索して出てくるようなMySQLの公式ドキュメントを指します。たとえば8系でいうならこちらです。

2について、アップグレードチェッカーでERROR判定となったチェック項目が1件以上存在すると、アップグレードは失敗します。その場合、ERRORメッセージを読んで修正をして、再度アップグレードを実行することになります。
WARNING以下の重大度のメッセージは、修正しなくてもアップグレードは成功しますが、指摘事項として挙げられます。(重大度については後述します)

上記のようなアップグレードチェッカーで確認されている項目は、チェッカーのログに記載された対応方法を参考に対処できます。
対して、アップグレードチェッカーの範囲外の非互換性は、別途調査が必要になります。 仮に、アップグレードチェッカーの存在に気づかずに対応を進めていると、チェッカーのログを見れば対応方法が書いてあるのに見逃してしまい、もったいないことになります。

3のように、非互換性リストとアップグレードチェッカーのログを突き合わせることで、追加調査が必要な項目にのみ工数をかけることができます。

4について、DB側で適切な事前対応が行われていることで、アプリとDB合わせての動作確認・性能試験がスムーズに進むというメリットがあります。


アップグレードチェッカー活用時に理解すべきこと

MySQLアップグレードチェッカーとAWS上のアップグレード事前チェックの違い

AWS公式Doc英語版には、下記のように記載があります。

The prechecks include some that are included with MySQL and some that were created specifically by the Amazon RDS team. [訳] 事前チェックには、MySQL に含まれているものと、Amazon RDS チームが特別に作成したものがあります。

AWSサポートに「AWSで作成している独自項目のリストは存在するか?」と質問したところ、『開示していない』との回答でした。(2024-10-21時点)
独自項目のみのリストは存在しないのですが、Auroraの事前チェック項目リストは発見したので、RDSで検証している際にも参考にしていました。


MySQLアップグレードチェッカーのソースコードと、RDSアップグレード事前チェックのログをログを参照した結果、RDSアップグレード事前チェックには下記が含まれることがわかりました。

  1. MySQLアップグレードチェッカーの、ターゲットバージョンに対するチェック項目
  2. MySQLアップグレードチェッカーの、ターゲットよりも後のバージョンに対するチェック項目
  3. AWSによる独自項目

察するに、2と3については、AWSがよしなにやってくれている部分でしょうね。

注意してほしいのが、1についてMySQLアップグレードチェッカーのソースコード側にはあるけれどAWSの事前チェックでは抜けている項目が多少あります
こちらもAWSがある意味よしなにスキップしているのかもしれませんね。。(ちょっと困りますが)
MySQLアップグレードチェッカーとAWS上のアップグレード事前チェックの差分まで追いかけて調査する場合は、こちらも突き合わせることになります。
たとえば、私が担当したMySQL 5.7->8.0.39の移行では、3つの項目が抜けていたので別途調査対応しました。

前提としては、アップグレードチェッカーの活用例でも示した通り、「MySQL公式Docベースの非互換性リストとAWS上のアップグレード事前チェックログの突き合わせ」で差分を対応することが優先だと思います。
アップグレードの目的から逆算するに、非互換性をつぶして期待した動作・性能を担保することが目的であり、MySQL公式のアップグレードチェッカーと全く同じチェック項目を実施すること自体が目的ではないからです。
しかし、間接的には最終目的につながりますし、差分があると気になる&調査工数も確保できるという方は、しっかり追いかけていくとよいと思います。

差分を追いかける場合は、アップグレードチェッカーのソースコードでタグをターゲットバージョンに変更して確認することに注意してください。
masterブランチでタグを指定せずに見ていると、今回関係ないバージョンの情報を含んでしまいます。(ここで1敗しました)

アップグレードチェッカーの重大度

アップグレードチェッカーで指摘される事項には、下記のような重大度が存在します。

ERROR : Correct these issues before upgrading to avoid compatibility issues. WARNING : No fatal errors were found that would prevent an upgrade, but some potential issues were detected.
NOTICE : No known compatibility errors or issues were found. But you can check the NOTICE Level error logs.
 
[訳]
ERROR : 互換性の問題を避けるため、アップグレード前にこれらの問題を修正してください。
WARNING : アップグレードを妨げる致命的なエラーは見つかりませんでしたが、潜在的な問題がいくつか検出されました。
NOTICE : 既知の互換性エラーや問題は見つかりませんでした。しかし、NOTICEレベルのエラーログを確認できます。

前述したようにアップグレードが失敗となる原因はERRORのみです。
WARNINGNOTICEについては、対応すべきかどうか調査・検討して結論を出しましょう。


RDSアップグレード事前チェックの仕様

私が経験したのはRDSのみなので、RDSアップグレード事前チェックの仕様を記載します。
公式ドキュメントの引用のみで十分だと考えた項目と、コメントを付記している項目があります。
引用部分はすべてこちらを参照しています。
MySQL 5.7->8系へのアップグレードを前提としたドキュメントですが、基本的な挙動として参考になると考えています。

停止前に事前チェックが実行されるので、ダウンタイムは発生しない(リソース負荷は発生する)

DB インスタンスがアップグレードで停止される前に事前チェックが実行されます。つまり、実行時にダウンタイムが発生することはありません。
事前チェックで非互換性が見つかった場合、DB インスタンスが停止する前に、Amazon RDSにより自動的にアップグレードがキャンセルされます。
(中略)
事前チェックの性質上、データベース内のオブジェクトが分析されます。この分析によりリソースが消費され、アップグレードが完了するまでの時間が長くなります。


事前チェック結果のログ、その他のログ

PrePatchCompatibility.logがいわゆるアップグレード事前チェックのログで、upgradeFailure.logは私が検証した際は出力されないままアップグレードが成功しました。

Amazon RDS は、ログファイル PrePatchCompatibility.log に各非互換性に関する詳細情報を記録します。
(中略)
通常、DB インスタンス内のデータベースとターゲットの MySQL バージョン間のメタデータに非互換性があるため、アップグレードは失敗します。
アップグレードが失敗した場合、upgradeFailure.log ファイルでこのような互換性に関する詳細を確認できます。
アップグレードを再試行する前に、非互換性を解決してください。


アップグレード失敗時の挙動

アップグレードが失敗することがあります。
特に、事前チェックでキャプチャされなかった非互換性がデータディクショナリに含まれていると、失敗する可能性があります。
この場合、データベースは新しい MySQL 8.0 バージョンで正常に起動できません。この時点で、Amazon RDS は、アップグレードに対して実行された変更をロールバックします。
ロールバック後、MySQL DB インスタンスは MySQL バージョン 5.7 を実行しています。 アップグレードが失敗してロールバックされると、Amazon RDS は、イベント ID RDS-EVENT-0188 のイベントを生成します。
(中略)
アップグレードの試行とロールバックが失敗すると、DB インスタンスが再起動されます。保留中のパラメータの変更は、再起動時に適用され、ロールバック後も保持されます。


アップグレード中にslow_loggeneral_logは空になる

MySQL のメジャーバージョンアップグレード中、必要に応じて Amazon RDS によって MySQL バイナリ mysql_upgrade が実行され、テーブルがアップグレードされます。
また、メジャーバージョンアップグレード中に Amazon RDS によって slow_log および general_log テーブルが空にされます。
ログ情報を保持するには、メジャーバージョンアップグレードの前にログファイルの内容を保存します。


RDSアップグレード事前チェックの検証手順例

参考までに、私が行なった検証の手順を参考までに記載しておきます。

  1. 検証用RDSのアップグレード時に使用するターゲットバージョン用のパラメータグループを用意する
  2. 複製前に本番RDSで、テーブル一覧を取得
  3. 本番RDSの最新スナップショットから本番の複製として検証用RDSを作成
  4. 検証用RDSでテーブル一覧を取得し、本番と差分がないことを確認
  5. ターゲットバージョンへのアップグレードを実施
  6. アップグレード失敗後にPrePatchCompatibility.logとエラーログを取得(出力されている場合は、upgradeFailure.logも)
  7. RDSを停止または削除し、上記ログで指摘された事項への対応策を調査・対応実施
  8. 再度アップグレードを実施(成功するまで続ける)


補足します。
検証のコストの見積もりが必要な場合の参考情報を記載します。 AWS公式Docを参考にすると、下記が課金対象です。

  • インスタンスクラスに対するオンデマンド料金(停止時はかからない)
  • ストレージ料金(停止時も課金)
  • 延長サポート料金(EOL後のバージョンのみ。サポートに確認したところ、停止時はかからない)

ストレージ料金を節約するため、本番RDSよりも少ないストレージサイズで複製しようと思ったのですが、同等サイズもしくはサイズをあげる変更しか許されないようでした。。(ここでも1敗)

1で、パラメータグループを用意する際は、現状使用しているパラメータグループとターゲットバージョンのデフォルトターゲットグループを比較して、前者で明示的に指定しているパラメータは後者にも反映していました。
明示的に指定しない場合のデフォルト値変更は、おそらくMySQL非互換性のドキュメントでも言及があると思いますので、新しいターゲットグループで意図通りの設定値になるようにしましょう。
また、RDSはマネージドサービスであるため、MySQLドキュメントだと明示的にパラメータを〇〇に指定しろと指示があっても、RDSの仕様で指定ができない場合があります。
指定ができない場合などについて、念の為AWSサポートに確認することをおすすめします。(私はいくつか聞いたところ、お客様側ですべきことはありませんので安心して、といった回答でした。)
このあたりも事前に調べてパラメータグループを準備しておくと、スムーズに検証ができます。

4で、ターゲットバージョンへのアップグレードを実施する前に、リードレプリカを作成してそちらでアップグレードを実施することもできます。
これにより古いバージョンのRDSも残るのでアップグレードの再試行がしやすいですが、料金に気をつけてください。

5に記載したPrePatchCompatibility.logなどのログは、コンソールからだと対象RDSクリック > ログとイベントで簡単にダウンロードできます。

一番ハマった点がありまして。。
PrePatchCompatibility.logで指摘された項目で、ViewのTIMESTAMP型カラムについてゼロ日付を使用しているという指摘がありました。
項目名は、17) Zero Date, Datetime, and Timestamp valuesです。
後続のバージョンで修正されたバグだと思われるのですが、これが誤った指摘であるということに気がつくのに時間がかかりました。
完全一致はしないのですが、似たようなバグの報告はこちら
稀にそういうケースもあるので気をつけて、という意味で記載しておきました。


おまけ - 調査時に役立つTips

AWS上のアップグレード事前チェックの範囲外を自身で調査する際に、INFORMATION_SCHEMAのテーブルを見にいくことがあります。
私はあまり経験がなかったので、勉強になったメモとして残しておきます。
うまいこと組み合わせると意図通りの情報を取得できると思います。

調査時に役立つSQLクエリ

システム系ではないかつビューではない、全テーブルの情報を取得するクエリ

SELECT 
    TABLE_SCHEMA, 
    TABLE_NAME, 
    TABLE_TYPE, 
    ENGINE 
FROM 
    INFORMATION_SCHEMA.TABLES 
WHERE 
    TABLE_SCHEMA NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys') 
    AND TABLE_TYPE != 'VIEW';


ビュー一覧を取得するクエリ

VIEW_DEFINITIONを出力すると長くなって見づらくなるので、必要に応じて使用してください。

SELECT 
    TABLE_SCHEMA, 
    TABLE_NAME, 
    VIEW_DEFINITION 
FROM 
    INFORMATION_SCHEMA.VIEWS;


全テーブルのカラム一覧を取得するクエリ

SELECT 
    T.TABLE_NAME, 
    C.COLUMN_NAME
FROM 
    INFORMATION_SCHEMA.TABLES T
JOIN 
    INFORMATION_SCHEMA.COLUMNS C 
ON 
    T.TABLE_NAME = C.TABLE_NAME;


システム系ではない、ストアドルーチン(ストアドプロシージャ、ストアドファンクション)を取得するクエリ

ROUTINE_DEFINITIONに、ストアドルーチンの処理内容が記載されています。
たとえば、アプリケーションのソースコード内のSQLステートメントに対しての予約語チェックはやったけど、ストアドルーチン見逃してた...なんてことがないように要注意です。

SELECT 
    ROUTINE_SCHEMA, 
    ROUTINE_NAME, 
    ROUTINE_TYPE, 
    ROUTINE_DEFINITION 
FROM 
    INFORMATION_SCHEMA.ROUTINES 
WHERE 
    ROUTINE_SCHEMA NOT IN ('sys', 'mysql');


SQL実行結果を見やすくするTips

SQLの末尾を;ではなく、\Gにすると、下記のように見やすく表示できます。
行ごとに開いて表示してくれるイメージなので、縦に長くはなります。。
どんなテーブル・カラムなんだろう?と確認する時に便利です。

*************************** 329. row ***************************
  TABLE_CATALOG: def
   TABLE_SCHEMA: performance_schema
     TABLE_NAME: innodb_redo_log_files
     TABLE_TYPE: BASE TABLE
         ENGINE: PERFORMANCE_SCHEMA
        VERSION: 10
     ROW_FORMAT: Dynamic
     TABLE_ROWS: 1
 AVG_ROW_LENGTH: 0
    DATA_LENGTH: 0
MAX_DATA_LENGTH: 0
   INDEX_LENGTH: 0
      DATA_FREE: 0
 AUTO_INCREMENT: NULL
    CREATE_TIME: 2024-11-12 01:17:41
    UPDATE_TIME: NULL
     CHECK_TIME: NULL
TABLE_COLLATION: utf8mb4_0900_ai_ci
       CHECKSUM: NULL
 CREATE_OPTIONS:
  TABLE_COMMENT:


また、下記コマンドを入力してからSQL実行すると、less形式で表示できます。 なが〜〜いSQL実行結果を閲覧したい時に使えます。
nopagerで解除も可能です。

-- less -Sで画面幅より長い行を折り返さない
pager less -S
--解除
nopager


調査時に役立つ VSCodeの機能

画像のように、VSCodeの検索画面で特定の単語を検索し、エディターで開くを押すと検索結果をアウトプットできます。
右上の1は検索結果の前後1行もアウトプットすると言う意味で、数字を変更できます。 該当行のみを表示したい場合はみたいなマークを押せばOKです。
ソースコードからある単語を含むSQLステートメントを検索&アウトプットして、スプレッドシートやその他のドキュメントで加工して調査を進めることができます。

※画像は、RDSアップグレード事前チェックで表示されるファイルの、チェック項目のタイトル部分のみを表示しており。特定の内部情報は含みません。あくまで例としてご覧ください。

検索アウトプット方法


ひとこと

はい、というわけで数年後に大きく仕様が変わっていないことを祈りつつまとめました。
EOL対応は大変だと思いますが、一度実績解除するとさまざまな局面で役に立つと思います。

一部環境で延長サポートを受けているMySQLのDBをお持ちの方は、早速アップグレードの際に試してみてくださいませ!

それでは、また逢う日まで、
サヨナラ、サヨナラ、サヨナラ...