`events_statements_summary_by_digest` で重いクエリを特定する
状況
ディレクターさんから、数日後にとあるレガシーサイトにアクセスが集中することが予定されており、負荷対策できないかどうかご連絡。
このサイトでは、過去に緊急でDBのスペックアップ対応を行った経緯があります。
その時の調査では、WEBサーバーのCPUやI/Oには問題が見られなかったものの
DBのCPUが張り付いている&DBのconnectionが跳ねていることが確認されていました。
今回負荷対策するにあたって、原因となっている重たいクエリがないか探して、改善したいところです。スペックアップは想像以上にコストがかかります。
試したこと
まずは下記からあたってみようと考えました。
- AWSのPerformance Insights
- スロークエリログを確認
ですが、
- Performance Insightsの無料枠は7日間保持であり、前回アクセスが集中した日付の情報が取得できませんでした。
- スロークエリログを取得していませんでした。もちろんCloudWatch Logsに出力もしていなかったのです。
代替案
Codexと相談の結果、MySQLのperformance_schema.events_statements_summary_by_digestを直接クエリすることが、必要な情報を補填してくれることがわかりました。
まず実行したのは次のクエリです。
SELECT
DIGEST_TEXT,
COUNT_STAR,
ROUND(SUM_TIMER_WAIT/1000000000000, 3) AS total_sec, -- 10^12 (picoseconds to seconds)
ROUND(AVG_TIMER_WAIT/1000000000000, 6) AS avg_sec -- 10^12 (picoseconds to seconds)
FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 20;
events_statements_summary_by_digestとは
MySQLのPerformance Schemaにある統計テーブルです。
Performance Schemaというのが、MySQLの内部動作を計測・可視化するための監視用仕組みなのでその一環ですね。
このテーブルは、同じ「クエリ形状」をまとめて統計化するもので、次の特徴があります。
- SQLのリテラル値は
?に正規化される -
DIGEST_TEXTは長文だと途中で切れる(これはPerformance Insightsと同じ) - 集計期間は統計リセット後からの累積
PIも結局このテーブルから情報を取得している
のですが、直接クエリを流せば集計期間はリセット時からなので、長めのデータがとれます。
重要なカラムは次の通りです。
-
DIGEST_TEXT: 正規化SQL -
COUNT_STAR: 実行回数 -
SUM_TIMER_WAIT: 累積実行時間(ピコ秒) -
AVG_TIMER_WAIT: 平均実行時間
どう読むか
1. まずはSUM_TIMER_WAITを見る
CPU影響は、だいたい「単価 × 回数」の総和で決まります。叩かれている回数が多いほど、アクセス集中の時間帯を含めユーザーが頻繁に踏むクエリだと判断できます。
2. 次にCOUNT_STARとAVGをセットで見る
-
AVGが高くCOUNTも高い: 最優先で対応 -
AVGは高いがCOUNTが低い: ピーク時の同時実行に注意 -
AVGは低いがCOUNTが極端に高い: 積み上げで効いてくる
3. 上位3〜5件に照準を絞る
すべてを最適化の対象にするのは現実的ではなさそうです。
実例
ある環境での上位は次のような構成でした。
- 検索一覧クエリ(
SQL_CALC_FOUND_ROWSを含む)-
COUNT_STAR: 23万回超 -
total_sec: 13万秒超 -
avg_sec: 約0.57秒
-
- Ngram全文検索クエリ
-
COUNT_STAR: 1万回超 -
total_sec: 2万秒超 -
avg_sec: 約1.8秒
-
0.57秒、1.8秒はCPUに負荷を与えている、改善の余地のあるクエリと判断できそうです。これとPIのリアルタイムで取得できるスロークエリを見て、問題のあるクエリを突き止めていきます。
DIGEST_TEXTが途中で切れる場合の補完方法
DIGEST_TEXTは省略されます。
SHOW VARIABLES LIKE 'performance_schema_max_sql_text_length';
SHOW VARIABLES LIKE 'performance_schema_max_digest_length';
それぞれデフォルトは1024の設定で、長文クエリは省略されてしまいます。
そんな時は、events_statements_history_longを見ればいいのですが
SELECT
TIMER_WAIT/1000000000000 AS sec,
SQL_TEXT
FROM performance_schema.events_statements_history_long
WHERE SQL_TEXT IS NOT NULL
ORDER BY TIMER_WAIT DESC
LIMIT 50;
このテーブルが問題の環境では見られませんでした。(詳細未確認)
ちなみに
クエリが遅い理由
- OFFSETが深すぎる
- JOINする段階でのテーブルが巨大すぎる(先に絞る)
- 全文検索クエリの駆動順・インデックスを再確認
このあたり、現在検討中です。
また、Performance Insightsですが、Database Insightsに移行されるとのこと。
学び
- Performance Insightsだけで情報が十分でないとき、
events_statements_summary_by_digestを直接見るのは有効 - AIエージェントとの相性で考えると、AWS CLIとperformance_schemaへのクエリ実行それぞれ調査が効率的
- まず
SUM_TIMER_WAITで上位を特定し、COUNT_STARとAVGで優先度を決める - 「遅い1本」より、「中程度 × 高頻度」がCPUを壊す
- よくある誤解として、
AVGが0.5秒台なら重くないと思いがちだが、高頻度なら十分重い(0.5秒 × 20万回は大きなCPU消費)
検証環境
RDS MySQL 8.0.32
一次情報へのリンク
MySQL Performance Schema 公式ドキュメント
AWS RDS Performance Insights ドキュメント
MySQL SQL DIGEST
未検証の範囲
-
events_statements_history_longを見れるようにする設定方法 -
performance_schemaにある他の魅力的な情報
更新履歴
- 2026-03-01: 初版
Discussion