そういう状況でスロークエリログを有効にするのは正しいアプローチです。ただ、「今まさに詰まっているクエリ」や「ロック待ちで止まっているスレッド」は、過去の記録を見るスロークエリログでは掴みにくいことがあります。
この記事では、MySQL 8.0~8.4 LTS を対象に、MySQL に標準搭載されている performance_schema を使って、遅いクエリとロック待ちを特定する手順を解説します。RHEL 9系・Ubuntu 24.04 LTS を想定環境として説明します。コピー&ペーストで使えるSQLと調査の流れを順に紹介していきます。
この記事のポイント
・performance_schema は MySQL 8.0 以降でデフォルト有効で追加インストール不要
・events_statements_summary_by_digest で実行回数・平均実行時間が上位のSQLを特定できる
・events_waits と threads テーブルでロック待ちとスレッド滞留状態を調査できる
・スロークエリログは過去の記録、performance_schema は現在の状態を確認するための使い分けが大切
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
MySQL performance_schema とは何か
performance_schema は、MySQL 5.5 から導入されたパフォーマンス計測用の特殊なデータベースです。MySQL 8.0 以降はデフォルトで有効になっており、追加のインストールや設定なしにすぐ使い始められます。仕組みとしては、MySQL がクエリを実行したり接続を処理したりするたびに、その実行時間・待機状態・ロック情報などを performance_schema の各テーブルにメモリ上で自動記録します。これを参照することで、「今何が遅いのか」「どこで待ちが発生しているのか」を MySQL の内側から直接調べられます。
主なテーブルの役割は次のとおりです。
・events_statements_summary_by_digest:SQLのパターン(ダイジェスト)ごとに実行回数・平均実行時間・検索行数を集計
・events_statements_current:現在実行中のクエリのリアルタイム情報
・events_waits_summary_global_by_event_name:待機イベントの種類ごとの累計を集計
・threads:接続中スレッドの状態(待機中・実行中・ロック待ちなど)
・setup_consumers:データ収集先の有効・無効を制御する設定テーブル
information_schema とは違い、performance_schema は「起動から現在までの動作記録」を持ちます。MySQL を再起動すると多くのテーブルはリセットされるため、調査は再起動前に行うのが基本です。
20年以上にわたって Linux サーバー上で MySQL・MariaDB を運用してきた経験から言うと、スロークエリログと performance_schema の2つを組み合わせることで、「過去の傾向」と「今の状態」の両方を把握できる調査体制が整います。
performance_schema が有効かどうかを確認する
1. 有効化の状態を確認する
まず、performance_schema が有効になっているか確認します。MySQL に接続してから次のクエリを実行してください。SHOW VARIABLES LIKE 'performance_schema';
+--------------------+-------+ | Variable_name | Value | +--------------------+-------+ | performance_schema | ON | +--------------------+-------+
2. 無効になっている場合の有効化
「OFF」になっている場合は、my.cnf(または my.ini)に次の行を追加して MySQL を再起動します。# /etc/my.cnf または /etc/mysql/mysql.conf.d/mysqld.cnf を編集 [mysqld] performance_schema = ON
# RHEL 9系の場合 systemctl restart mysqld # Ubuntu 24.04 の場合 systemctl restart mysql
遅いクエリを performance_schema で特定する
1. 実行回数・実行時間で上位のクエリを確認する
events_statements_summary_by_digest は、SQL のパターン(ダイジェスト)ごとに実行回数や平均実行時間を集計しています。まずここを見ることで、「何度も実行されていて遅いSQL」を絞り込めます。SELECT SCHEMA_NAME AS データベース名, LEFT(DIGEST_TEXT, 80) AS SQL概要, COUNT_STAR AS 実行回数, ROUND(AVG_TIMER_WAIT / 1000000000000, 3) AS 平均実行時間sec, ROUND(MAX_TIMER_WAIT / 1000000000000, 3) AS 最大実行時間sec, ROUND(SUM_ROWS_EXAMINED / COUNT_STAR, 0) AS 平均検索行数 FROM performance_schema.events_statements_summary_by_digest WHERE SCHEMA_NAME IS NOT NULL AND COUNT_STAR > 10 ORDER BY AVG_TIMER_WAIT DESC LIMIT 10;
+---------------+-----------------------------------+-----------+-----------------+------------------+--------------+ | データベース名 | SQL概要 | 実行回数 | 平均実行時間sec | 最大実行時間sec | 平均検索行数 | +---------------+-----------------------------------+-----------+-----------------+------------------+--------------+ | myapp | SELECT * FROM orders WHERE user...| 1420 | 2.345 | 8.901 | 980340 | | myapp | UPDATE sessions SET updated_at... | 8203 | 0.231 | 1.202 | 1 | +---------------+-----------------------------------+-----------+-----------------+------------------+--------------+
「平均実行時間sec」が大きく「実行回数」も多い SQL が最優先の改善候補です。平均は低くても「最大実行時間sec」が突出している場合は、特定の条件やデータ量のときだけ遅くなるクエリが疑われます。
2. 実行中のクエリをリアルタイムで確認する
今まさに実行されているクエリは events_statements_current で確認できます。DB が重くなっている最中に調査するときに有効です。SELECT t.PROCESSLIST_ID AS プロセスID, t.PROCESSLIST_DB AS データベース, t.PROCESSLIST_STATE AS 状態, ROUND(e.TIMER_WAIT / 1000000000000, 2) AS 実行時間sec, LEFT(e.SQL_TEXT, 100) AS SQL概要 FROM performance_schema.events_statements_current e JOIN performance_schema.threads t ON e.THREAD_ID = t.THREAD_ID WHERE e.SQL_TEXT IS NOT NULL ORDER BY e.TIMER_WAIT DESC LIMIT 20;
3. 統計をリセットして現状を測り直す
サーバーの起動から時間が経っている場合、古いデータが混じっています。リセットしてから再計測すると、今の状況を正確に把握しやすくなります。# 累計統計をリセット(MySQL サービスの再起動は不要) TRUNCATE TABLE performance_schema.events_statements_summary_by_digest;
ロック待ちと待機状態を調査する
1. 待機イベントの集計を確認する
遅いクエリが見当たらないのに DB が重い場合、ロック待ちや I/O 待ちが原因であることがあります。events_waits_summary_global_by_event_name を見ると、どの種類の待機が多いかが分かります。SELECT EVENT_NAME AS 待機イベント名, COUNT_STAR AS 発生回数, ROUND(AVG_TIMER_WAIT / 1000000, 2) AS 平均待機マイクロ秒, ROUND(SUM_TIMER_WAIT / 1000000000000, 3) AS 合計待機sec FROM performance_schema.events_waits_summary_global_by_event_name WHERE COUNT_STAR > 0 AND EVENT_NAME NOT LIKE 'idle%' ORDER BY SUM_TIMER_WAIT DESC LIMIT 20;
・wait/synch/mutex/innodb/:InnoDB の内部ロック。頻度が高ければ InnoDB の競合が疑われる
・wait/io/file/innodb/:InnoDB のファイル I/O 待ち。ディスク速度の問題や innodb_buffer_pool_size の不足が考えられる
・wait/lock/table/:テーブルロック待ち。MyISAM や DDL 実行中に発生しやすい
・wait/synch/rwlock/:読み書きロック。並列読み込みが多い環境で発生する
2. threads テーブルでスレッドの状態を確認する
今まさに待ちが発生しているスレッドを調べるには threads テーブルが便利です。SELECT PROCESSLIST_ID AS プロセスID, PROCESSLIST_USER AS ユーザー, PROCESSLIST_DB AS データベース, PROCESSLIST_STATE AS 状態, PROCESSLIST_TIME AS 経過時間sec, LEFT(PROCESSLIST_INFO, 80) AS 実行中SQL FROM performance_schema.threads WHERE TYPE = 'FOREGROUND' AND PROCESSLIST_STATE IS NOT NULL AND PROCESSLIST_STATE NOT IN ('', 'Sleep') ORDER BY PROCESSLIST_TIME DESC LIMIT 20;
「経過時間sec」が大きいのに状態が「Sleep」以外のスレッドが複数ある場合は、単一の長時間クエリが他のクエリをブロックしている典型的なパターンです。
スロークエリログと performance_schema の使い分け
スロークエリログと performance_schema は、どちらも遅いクエリを調査するためのツールですが、役割が異なります。・スロークエリログ:過去に遅かったクエリをファイルに記録する。MySQL の再起動後でも残る。しきい値(long_query_time)を超えたクエリだけが対象
・performance_schema:現在の実行状態をメモリ上で記録する。再起動でリセットされる。しきい値に関わらずすべてのクエリが集計対象
定常的に遅いクエリの傾向を把握するにはスロークエリログを使い、「今なぜ重いのか」をリアルタイムで調査するには performance_schema を見るのが効率的です。
Linux サーバー上での自前運用では、両方を有効にしておき状況に応じて使い分けるのが現場での基本的な考え方です。MySQLのパフォーマンス調査の全体像や実践的な運用ノウハウについては、Linux Master Pro Seminarのカリキュラムでも体系的に扱っています。
「データが蓄積されない・集計が空」への対処
performance_schema のテーブルを見ても集計が空、または DIGEST が NULL の行しかない場合は、次の2点を確認します。1. コンシューマーの有効化状態を確認する
performance_schema は「インストゥルメント(計測対象)」と「コンシューマー(収集先)」の2段階で制御されています。コンシューマーが無効になっていると、テーブルにデータが蓄積されません。-- コンシューマーの有効化状態を確認 SELECT NAME, ENABLED FROM performance_schema.setup_consumers WHERE NAME LIKE '%statements%';
+---------------------------------+---------+ | NAME | ENABLED | +---------------------------------+---------+ | events_statements_current | YES | | events_statements_history | YES | | events_statements_history_long | NO | +---------------------------------+---------+
UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME = 'events_statements_history_long';
2. DIGEST が NULL になる理由と対処
events_statements_summary_by_digest の DIGEST_TEXT が NULL の行は、ダイジェストのキャッシュ(performance_schema_digests_size)が上限に達した場合に記録されます。この NULL 行は「キャッシュからあふれた SQL の集計」を意味しています。解消するには、my.cnf で次の値を大きくして再起動します。
# /etc/my.cnf の [mysqld] セクション # デフォルト 10000。SQL の種類が多い環境では増やす performance_schema_digests_size = 20000
本記事のまとめ
performance_schema を使ったパフォーマンス調査の主要な SQL をまとめます。| やりたいこと | 使うテーブル・コマンド |
|---|---|
| 実行回数・実行時間が上位のSQLを特定 | SELECT ... FROM events_statements_summary_by_digest ORDER BY AVG_TIMER_WAIT DESC |
| 現在実行中のクエリを確認 | SELECT ... FROM events_statements_current JOIN threads WHERE SQL_TEXT IS NOT NULL |
| 待機イベントの種類と頻度を集計 | SELECT ... FROM events_waits_summary_global_by_event_name ORDER BY SUM_TIMER_WAIT DESC |
| ロック待ちのスレッドを探す | SELECT ... FROM threads WHERE PROCESSLIST_STATE IS NOT NULL ORDER BY PROCESSLIST_TIME DESC |
| 統計をリセットして現状を測り直す | TRUNCATE TABLE events_statements_summary_by_digest |
| コンシューマーの有効化状態を確認 | SELECT NAME, ENABLED FROM setup_consumers WHERE NAME LIKE '%statements%' |
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、
MySQL・MariaDB などデータベースサーバーの構築・運用から、performance_schema を使ったパフォーマンス調査の実践まで、Linux Master Pro Seminar では少人数ハンズオン形式で体系的に学べます。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:MySQLのinnodb_force_recoveryでInnoDBクラッシュ障害から復旧する手順|起動不能なDBからデータを救出する方法
- この記事の属するカテゴリ:データーベース管理へ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます