🙌

`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;

このテーブルが問題の環境では見られませんでした。(詳細未確認)

ちなみに

クエリが遅い理由

  1. OFFSETが深すぎる
  2. JOINする段階でのテーブルが巨大すぎる(先に絞る)
  3. 全文検索クエリの駆動順・インデックスを再確認

このあたり、現在検討中です。

また、Performance Insightsですが、Database Insightsに移行されるとのこと。

RDS Performance Insights の CloudWatch Database Insights への統合が 2025 年 11 月 30 日から 2026 年 6 月 30 日に変更されました

学び

  • 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