🚀

Aurora MySQLのメモリ不足の原因を特定する

2024/05/07に公開

シンプルフォーム株式会社でインフラエンジニアをしている守屋です。

本記事では Aurora MySQL の OOM(メモリ不足)エラーについて、原因となるクエリを特定するために役立つ Tips を弊社での実例を交えてご紹介します。

発端

突如 Slack に鳴り響く不吉な通知。

「パターン青!障害です!!」

どうやら本番環境の Aurora クラスターがフェイルオーバーしてアプリケーションが DB コネクションエラーを引き起こした模様です。幸いインスタンスは冗長化していて Aurora のフェイルオーバーは高速であるため、ユーザー目線では瞬断が発生した程度の比較的影響が小さめな障害に留まりました。しかしインフラエンジニアとしては捨ておけない状況です!早速原因の調査を始めました。

フェイルオーバーの原因

結論から言うとメモリ使用量がスパイクして OOM エラーが発生したことが原因でした。根拠としては Aurora MySQL のエラーログで、障害発生時刻にて以下のような出力を確認できました。

20xx-xx-xxx:xx:xxx xxxx [Note] Aborted connection xxxx to db: 'xxxx' user: 'xxxx' host: 'xx.x.x.xx' (Got an error reading communication packets)
OOM crash avoidance result:  success: yes  num success: 59  system KB: 32512132  available KB: 3447982  low-threshold KB: 1625606  recovery time: 0  num declined query: 0  num killed query: 0  num killed connection: 0

また、メトリクスを確認したところ FreeableMemory の値が減少しており、こちらからもメモリ使用量がスパイクしていることが伺えました。

その後の対応

取り急ぎ止血対応としてインスタンスのスケールアップを検討しました。とはいえ本質的には原因を特定して根本解決したいところ。アプリケーションが発行するクエリの中にボトルネックがあると推測されますが、どのようにクエリを特定すればよいでしょうか?真っ先に確認したのは Performance Insights です。以下の図は実際に障害が発生した際のモニタリング画面です。06:15 付近でフェイルオーバー開始したのですが、直前のグラフを見ても特にスパイクしている様子はなく正直、普段通りに見えました。トップ SQL にいくつか怪しいクエリは見受けられたものの「これが原因!」と確証を得るには至りませんでした。

一つの対応策

こんなときに設定しておくと役に立つ Aurora MySQL のパラメータがあります。

aurora_oom_response

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/CHAP_Troubleshooting.html#CHAP_Troubleshooting.AuroraMySQLOOM

こちらは DB インスタンスでメモリ不足が発生した場合に自動的に行われるアクションを設定することができるパラメータです。以下の 4 種類の対応を指定できます。(上記のドキュメント内での説明文そのままです)

アクション 説明
decline DB インスタンスがメモリ不足になると、新しいクエリを拒否します
kill_query インスタンスメモリが低いしきい値を超えるまで、メモリ消費の降順でクエリを終了します。DDL ステートメントは終了されません。詳細については、MySQL ドキュメントの KILL ステートメントを参照してください。
print 大量のメモリを使用するクエリのみを出力します。
tune 内部テーブルキャッシュを調整して、メモリをシステムに戻します。Aurora MySQL は、メモリが少ない状態では table_open_cache および table_definition_cache などのキャッシュに使用されるメモリを削減します。最終的に、Aurora MySQL は、システムのメモリ不足がなくなると、メモリ使用量を通常に戻します。詳細については、MySQL ドキュメントの「table_open_cache」と「table_definition_cache」を参照してください。

ドキュメントにありますが、メモリが 4GiB 以上のインスタンスの場合はデフォルトで無効化されており、弊社のインスタンスもこれに該当していました。弊社では原因となるクエリを特定するため、クラスターパラメータグループにてprint を指定しています。ちなみにカンマ区切りで複数のアクションを指定することも可能です。

設定した状態で OOM が発生した場合、エラーログに原因となったクエリの情報が出力されます。

OOM crash avoidance: kill option to avoid low memory: MySQL thread id 16554, OS thread handle, query id <クエリID> <ホストIP> <DB USER名> SELECT hoge from fuga...(原因クエリ)
OOM crash avoidance result:  success: yes  num success: 57  system KB: 32512136  available KB: 4037960  low-threshold KB: 1625606  recovery time: 1  num declined query: 0  num killed query: 0  num killed connection: 0

本パラメータのおかげで原因となるクエリを特定することができました!
(特定後のクエリのチューニングの話は別の機会に記事にできればと思います)

おわりに

RDS のパフォーマンスについては CPU 使用率を注視することが多いかと思いますが、メモリの使用量がネックになることもあります。例えば以下のように一時テーブルが内部的に作成される場合です。

MySQL は、GROUP BY、ORDER BY、DISTINCT、UNION などの一部のタイプのクエリを処理しながら、中間結果を保存するための内部一時テーブルを作成します。これらの内部一時テーブルは、最初にメモリに作成され、最大サイズに達するとディスク上のテーブルに変換されます。

https://cloud.google.com/mysql/memory-usage?hl=ja

怪しいクエリを EXPLAIN した結果の Extra 欄に Using temporary と出ている場合はこの一時テーブルが作成されると判断できます。

https://dev.mysql.com/doc/refman/8.0/ja/explain-output.html

本記事では aurora_oom_response というパラメータを設定して原因となるクエリを特定した例をご紹介しました。少しでも参考になれば幸いです。

SimpleForm Tech Blog

Discussion